/// <summary> /// This method creates gradien color with brightnes. /// </summary> /// <param name="beginColor">Start color for gradient.</param> /// <param name="position">Position used between Start and end color.</param> /// <returns>Calculated Gradient color from gradient position</returns> private static SKColor GetBrightGradientColor(SKColor beginColor, double position) { position *= 2; double brightness = 0.5; if (position < brightness) { return(ChartGraphics.GetGradientColor(Color.FromArgb(beginColor.Alpha, (byte)255, (byte)255, (byte)255), beginColor, 1 - brightness + position)); } else if (-brightness + position < 1) { return(ChartGraphics.GetGradientColor(beginColor, SKColors.Black, -brightness + position)); } else { return(Color.FromArgb(beginColor.Alpha, (byte)0, (byte)0, (byte)0)); } }
/// <summary> /// Return intensity of lightStyle for Polygons. There are tree types of lights: None, /// Simplistic and Realistic. None Style have same lightStyle intensity on /// all polygons. Normal vector doesn’t have influence on this type /// of lighting. Simplistic style have lightStyle source, which is /// rotated together with scene. Realistic lighting have fixed lightStyle /// source and intensity of lightStyle is change when scene is rotated. /// </summary> /// <param name="points">Points of the polygon</param> /// <param name="surfaceColor">Color used for polygons without lighting</param> /// <param name="visiblePolygon">This flag gets information if polygon is visible or not.</param> /// <param name="rotation">Y angle ( from -90 to 90 ) Should be used width switchSeriesOrder to get from -180 to 180</param> /// <param name="surfaceName">Used for lighting of front - back and left - right sides</param> /// <param name="switchSeriesOrder">Used to calculate real y angle</param> /// <returns>Color corrected with intensity of lightStyle</returns> internal SKColor GetPolygonLight(Point3D[] points, SKColor surfaceColor, bool visiblePolygon, float rotation, SurfaceNames surfaceName, bool switchSeriesOrder) { // Corrected color SKColor color = surfaceColor; // Direction of lightStyle source Point3D lightSource; lightSource = new Point3D(0F, 0F, 1F); // There are tree different lightStyle styles: None, Simplistic and realistic. switch (_lightStyle) { // LightStyle style is None case LightStyle.None: { // Use same color break; } // LightStyle style is Simplistic case LightStyle.Simplistic: { // Find two vectors of polygon Point3D firstVector = new(); firstVector.X = points[0].X - points[1].X; firstVector.Y = points[0].Y - points[1].Y; firstVector.Z = points[0].Z - points[1].Z; Point3D secondVector = new(); secondVector.X = points[2].X - points[1].X; secondVector.Y = points[2].Y - points[1].Y; secondVector.Z = points[2].Z - points[1].Z; // Find Normal vector for Polygon Point3D normalVector = new(); normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y; normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z; normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X; // Polygon is left side ( like side of area chart ) if (surfaceName == SurfaceNames.Left) { color = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.15); } // Polygon is right side ( like side of area chart ) else if (surfaceName == SurfaceNames.Right) { color = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.15); } // Polygon is front side ( like side of area chart ) else if (surfaceName == SurfaceNames.Front) { color = surfaceColor; } // Polygon is back side ( like side of area chart ) else if (surfaceName == SurfaceNames.Back) { color = surfaceColor; } // Polygon has angle with bottom side ( Line chart or top of area chart ) else { float angleLeft; float angleRight; // Find angles between lightStyle and polygon for different y-axis angles. if (switchSeriesOrder) { if (rotation > 0 && rotation <= 90) { angleLeft = GetAngle(normalVector, _lightVectors[3]); angleRight = GetAngle(normalVector, _lightVectors[4]); } else { angleLeft = GetAngle(normalVector, _lightVectors[4]); angleRight = GetAngle(normalVector, _lightVectors[3]); } } else { if (rotation > 0 && rotation <= 90) { angleLeft = GetAngle(normalVector, _lightVectors[4]); angleRight = GetAngle(normalVector, _lightVectors[3]); } else { angleLeft = GetAngle(normalVector, _lightVectors[3]); angleRight = GetAngle(normalVector, _lightVectors[4]); } } if (Math.Abs(angleLeft - angleRight) < 0.01) { color = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.25); } else if (angleLeft < angleRight) { color = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.25); } else { color = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.15); } } break; } // LightStyle style is Realistic default: { // Find two vectors of polygon Point3D firstVector = new(); firstVector.X = points[0].X - points[1].X; firstVector.Y = points[0].Y - points[1].Y; firstVector.Z = points[0].Z - points[1].Z; Point3D secondVector = new(); secondVector.X = points[2].X - points[1].X; secondVector.Y = points[2].Y - points[1].Y; secondVector.Z = points[2].Z - points[1].Z; // Find Normal vector for Polygon Point3D normalVector = new(); normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y; normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z; normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X; // ****************************************************************** // Color correction. Angle between Normal vector of polygon and // vector of lightStyle source is used. // ****************************************************************** if (surfaceName == SurfaceNames.Front) { lightSource.Z *= -1; color = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[2]) / Math.PI); } else if (surfaceName == SurfaceNames.Back) { lightSource.Z *= -1; color = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[1]) / Math.PI); } else { if (visiblePolygon) { lightSource.Z *= -1; } color = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, normalVector) / Math.PI); } break; } } return(color); }
/// <summary> /// Return intensity of lightStyle for 3D Cube. There are tree types of lights: None, /// Simplistic and Realistic. None Style have same lightStyle intensity on /// all polygons. Normal vector doesn’t have influence on this type /// of lighting. Simplistic style have lightStyle source, which is /// rotated together with scene. Realistic lighting have fixed lightStyle /// source and intensity of lightStyle is change when scene is rotated. /// </summary> /// <param name="surfaceColor">Color used for polygons without lighting</param> /// <param name="front">Color corrected with intensity of lightStyle for Front side of the 3D Rectangle</param> /// <param name="back">Color corrected with intensity of lightStyle for Back side of the 3D Rectangle</param> /// <param name="left">Color corrected with intensity of lightStyle for Left side of the 3D Rectangle</param> /// <param name="right">Color corrected with intensity of lightStyle for Right side of the 3D Rectangle</param> /// <param name="top">Color corrected with intensity of lightStyle for Top side of the 3D Rectangle</param> /// <param name="bottom">Color corrected with intensity of lightStyle for Bottom side of the 3D Rectangle</param> internal void GetLight(SKColor surfaceColor, out SKColor front, out SKColor back, out SKColor left, out SKColor right, out SKColor top, out SKColor bottom) { switch (_lightStyle) { // LightStyle style is None case LightStyle.None: { front = surfaceColor; left = surfaceColor; top = surfaceColor; back = surfaceColor; right = surfaceColor; bottom = surfaceColor; break; } // LightStyle style is Simplistic case LightStyle.Simplistic: { front = surfaceColor; left = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.25); top = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.15); back = surfaceColor; right = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.25); bottom = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0.15); break; } // LightStyle style is Realistic default: { // For Right Axis angle Realistic lightStyle should be different if (_rightAngleAxis) { // LightStyle source Vector Point3D lightSource = new(0F, 0F, -1F); Point3D[] rightPRpoints = new Point3D[1]; rightPRpoints[0] = lightSource; RightAngleProjection(rightPRpoints); // ****************************************************************** // Color correction. Angle between Normal vector of polygon and // vector of lightStyle source is used. // ****************************************************************** if (_angleY >= 45 || _angleY <= -45) { front = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[1]) / Math.PI); back = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[2]) / Math.PI); left = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0); right = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0); } else { front = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 0); back = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, 1); left = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[3]) / Math.PI); right = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[4]) / Math.PI); } top = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[5]) / Math.PI); bottom = ChartGraphics.GetGradientColor(surfaceColor, SKColors.Black, GetAngle(lightSource, _lightVectors[6]) / Math.PI); } else { // LightStyle source Vector Point3D lightSource = new(0F, 0F, 1F); // ****************************************************************** // Color correction. Angle between Normal vector of polygon and // vector of lightStyle source is used. // ****************************************************************** front = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[1]) / Math.PI); back = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[2]) / Math.PI); left = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[3]) / Math.PI); right = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[4]) / Math.PI); top = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[5]) / Math.PI); bottom = GetBrightGradientColor(surfaceColor, GetAngle(lightSource, _lightVectors[6]) / Math.PI); } break; } } }
/// <summary> /// Draws 3D button in the scroll bar /// </summary> /// <param name="graph">Chart graphics.</param> /// <param name="buttonRect">Button position.</param> /// <param name="pressedState">Indicates that button is pressed.</param> /// <param name="buttonType">Button type to draw.</param> internal void PaintScrollBar3DButton( ChartGraphics graph, SKRect buttonRect, bool pressedState, ScrollBarButtonType buttonType) { // Page Up/Down buttons do not require drawing if (buttonType == ScrollBarButtonType.LargeIncrement || buttonType == ScrollBarButtonType.LargeDecrement) { return; } // Get 3 levels of colors for button drawing var darkerColor = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.Black, 0.5); var darkestColor = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.Black, 0.8); var lighterColor = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.White, 0.5); // Fill button rectangle background graph.FillRectangleRel( buttonRect, _buttonCurrentColor, ChartHatchStyle.None, "", ChartImageWrapMode.Tile, SKColor.Empty, ChartImageAlignmentStyle.Center, GradientStyle.None, SKColor.Empty, darkerColor, (pressedState) ? 1 : 0, ChartDashStyle.Solid, SKColor.Empty, 0, PenAlignment.Outset); // Check if 2 or 1 pixel border will be drawn (if size too small) bool singlePixelBorder = Size <= 12; // Draw 3D effect around the button when not pressed if (!pressedState) { // Get relative size of 1 pixel SKSize pixelRelativeSize = new(1, 1); pixelRelativeSize = graph.GetRelativeSize(pixelRelativeSize); // Draw top/left border with button color graph.DrawLineRel( (singlePixelBorder) ? lighterColor : _buttonCurrentColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left, buttonRect.Bottom), new SKPoint(buttonRect.Left, buttonRect.Top)); graph.DrawLineRel( (singlePixelBorder) ? lighterColor : _buttonCurrentColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left, buttonRect.Top), new SKPoint(buttonRect.Right, buttonRect.Top)); // Draw right/bottom border with the darkest color graph.DrawLineRel( (singlePixelBorder) ? darkerColor : darkestColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Right, buttonRect.Bottom), new SKPoint(buttonRect.Right, buttonRect.Top)); graph.DrawLineRel( (singlePixelBorder) ? darkerColor : darkestColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left, buttonRect.Bottom), new SKPoint(buttonRect.Right, buttonRect.Bottom)); if (!singlePixelBorder) { // Draw right/bottom border (offset 1) with the dark color graph.DrawLineRel( darkerColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height), new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Top + pixelRelativeSize.Height)); graph.DrawLineRel( darkerColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height), new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height)); // Draw top/left border (offset 1) with lighter color graph.DrawLineRel( lighterColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height), new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Top + pixelRelativeSize.Height)); graph.DrawLineRel( lighterColor, 1, ChartDashStyle.Solid, new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Left + pixelRelativeSize.Height), new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Left + pixelRelativeSize.Height)); } } // Check axis orientation bool verticalAxis = (axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right); // Set graphics transformation for button pressed mode float pressedShifting = (singlePixelBorder) ? 0.5f : 1f; if (pressedState) { graph.TranslateTransform(pressedShifting, pressedShifting); } // Draw button image SKRect buttonAbsRect = graph.GetAbsoluteRectangle(buttonRect); float imageOffset = (singlePixelBorder) ? 2 : 3; switch (buttonType) { case (ScrollBarButtonType.SmallDecrement): { // Calculate triangal points position SKPoint[] points = new SKPoint[3]; if (verticalAxis) { points[0].X = buttonAbsRect.Left + imageOffset; points[0].Y = buttonAbsRect.Top + (imageOffset + 1f); points[1].X = buttonAbsRect.Left + buttonAbsRect.Width / 2f; points[1].Y = buttonAbsRect.Bottom - imageOffset; points[2].X = buttonAbsRect.Right - imageOffset; points[2].Y = buttonAbsRect.Top + (imageOffset + 1f); } else { points[0].X = buttonAbsRect.Left + imageOffset; points[0].Y = buttonAbsRect.Top + buttonAbsRect.Height / 2f; points[1].X = buttonAbsRect.Right - (imageOffset + 1f); points[1].Y = buttonAbsRect.Top + imageOffset; points[2].X = buttonAbsRect.Right - (imageOffset + 1f); points[2].Y = buttonAbsRect.Bottom - imageOffset; } using var brush = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor }; graph.FillPolygon(brush, points); break; } case (ScrollBarButtonType.SmallIncrement): { // Calculate triangal points position SKPoint[] points = new SKPoint[3]; if (verticalAxis) { points[0].X = buttonAbsRect.Left + imageOffset; points[0].Y = buttonAbsRect.Bottom - (imageOffset + 1f); points[1].X = buttonAbsRect.Left + buttonAbsRect.Width / 2f; points[1].Y = buttonAbsRect.Top + imageOffset; points[2].X = buttonAbsRect.Right - imageOffset; points[2].Y = buttonAbsRect.Bottom - (imageOffset + 1f); } else { points[0].X = buttonAbsRect.Right - imageOffset; points[0].Y = buttonAbsRect.Top + buttonAbsRect.Height / 2f; points[1].X = buttonAbsRect.Left + (imageOffset + 1f); points[1].Y = buttonAbsRect.Top + imageOffset; points[2].X = buttonAbsRect.Left + (imageOffset + 1f); points[2].Y = buttonAbsRect.Bottom - imageOffset; } using var brush = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor }; graph.FillPolygon(brush, points); break; } case (ScrollBarButtonType.ZoomReset): { // Draw circule with a minus sign using var pen = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor }; graph.DrawEllipse(pen, buttonAbsRect.Left + imageOffset - 0.5f, buttonAbsRect.Top + imageOffset - 0.5f, buttonAbsRect.Width - 2f * imageOffset, buttonAbsRect.Height - 2f * imageOffset); graph.DrawLine(pen, buttonAbsRect.Left + imageOffset + 1.5f, buttonAbsRect.Top + buttonAbsRect.Height / 2f - 0.5f, buttonAbsRect.Right - imageOffset - 2.5f, buttonAbsRect.Top + buttonAbsRect.Height / 2f - 0.5f); break; } } // Reset graphics transformation for button pressed mode if (pressedState) { graph.TranslateTransform(-pressedShifting, -pressedShifting); } }