/// <summary> /// Gets nearest point on the curve (or the line) to the provided point. /// </summary> /// <param name="points">Points of the curve (or the line)</param> /// <param name="p0">Some point to find the nearest one on the curve (on the line), i.e. projection.</param> /// <returns>Nearest point on the curve (or the line), i.e. projection of the point p0.</returns> private PointF GetProjectedPoint(IMapContext map, PointF[] points, PointF p0) { PointF[] nearest = points.OrderBy(n => map.DistanceBetweenPoints(n, p0)).Take(2).ToArray(); PointF p1 = nearest[0]; PointF p2 = nearest[1]; PointF e1 = new PointF(p2.X - p1.X, p2.Y - p1.Y); PointF e2 = new PointF(p0.X - p1.X, p0.Y - p1.Y); double val = e1.X * e2.X + e1.Y * e2.Y; double len = e1.X * e1.X + e1.Y * e1.Y; PointF p = new PointF((float)(p1.X + val * e1.X / len), (float)(p1.Y + val * e1.Y / len)); return(p); }
/// <summary> /// Shifts all points of the curve (or line) to the ancor point. /// </summary> /// <param name="points">Points of the curve (or line)</param> /// <param name="p0">Ancor point. All curve or line points will be corrected, /// so the shifted curve (or line) will intersect the ancor point.</param> /// <returns>Corrected points of the curve (or line)</returns> private PointF[] ShiftToAncorPoint(IMapContext map, PointF[] points, PointF p0) { if (p0 != PointF.Empty) { PointF proj = GetProjectedPoint(map, points, p0); if (map.DistanceBetweenPoints(proj, p0) > 1) { float dx = p0.X - proj.X; float dy = p0.Y - proj.Y; points = points.Select(p => new PointF(p.X + dx, p.Y + dy)).ToArray(); } } return(points); }
/// <summary> /// Performs a correction of inverse projection. /// Checks that horizontal coordinates of a point are correct, /// and in case if not correct, applies iterative algorithm for searching correct values. /// </summary> /// <param name="p">Point to check</param> /// <param name="hor">Horizontal coordinates of the point</param> /// <returns>Corrected horizontal coordinates</returns> private CrdsHorizontal CorrectInverse(PointF p, CrdsHorizontal hor) { PointF pLeftEdge = Project(new CrdsHorizontal(Map.Center.Azimuth - 90, hor.Altitude)); PointF pRightEdge = Project(new CrdsHorizontal(Map.Center.Azimuth + 90, hor.Altitude)); PointF pEdge; if (p.X < Map.Width / 2.0) { pEdge = pLeftEdge; } else { pEdge = pRightEdge; } Point origin = new Point((int)(Map.Width / 2.0), (int)(Map.Height / 2.0)); double edgeToCenter = Map.DistanceBetweenPoints(origin, pEdge); double currentToCenter = Map.DistanceBetweenPoints(origin, p); bool correctionNeeded = Math.Abs(Map.Center.Altitude) == 90 || currentToCenter > edgeToCenter; if (correctionNeeded) { // projected coordinates of a horizontal grid pole (zenith or nadir point) PointF pole = Project(new CrdsHorizontal(0, 90 * (Map.Center.Altitude > 0 ? 1 : -1))); double angleWhole = 360 - Map.AngleBetweenVectors(pole, pLeftEdge, pRightEdge); double angleLeft = Map.AngleBetweenVectors(pole, p, pLeftEdge); double angleRight = Map.AngleBetweenVectors(pole, p, pRightEdge); int shiftSign = angleLeft < angleRight ? -1 : 1; int poleFix = 1; if (Map.Center.Altitude == 90 && pole.Y < p.Y) { poleFix = -1; } else if (Map.Center.Altitude == -90 && pole.Y > p.Y) { poleFix = -1; } double poleAngle = Math.Min(angleLeft, angleRight); double azimuthShift = poleAngle / angleWhole * 180; PointF pCorrected = new PointF(0, 0); double distOriginal = Map.DistanceBetweenPoints(p, pEdge); double distCorrected = 0; int iterations = 0; do { hor = new CrdsHorizontal(Angle.To360(Map.Center.Azimuth + shiftSign * 90 + poleFix * shiftSign * azimuthShift), hor.Altitude); // corrected coordinates of a projected point pCorrected = Project(hor); distCorrected = Map.DistanceBetweenPoints(pCorrected, pEdge); if (distCorrected > 0) { azimuthShift *= distOriginal / distCorrected; } iterations++; }while (Map.DistanceBetweenPoints(p, pCorrected) > 2 && iterations < 5); } return(hor); }
public override void Render(IMapContext map) { Graphics g = map.Graphics; var allComets = cometsCalc.Comets; bool isGround = settings.Get("Ground"); bool useTextures = settings.Get("PlanetsTextures"); bool drawComets = settings.Get("Comets"); bool drawLabels = settings.Get("AsteroidsLabels"); Brush brushNames = new SolidBrush(map.GetColor("ColorCometsLabels")); if (drawComets) { var comets = allComets.Where(a => Angle.Separation(map.Center, a.Horizontal) < map.ViewAngle); foreach (var c in comets) { float diam = map.GetDiskSize(c.Semidiameter); if (diam > 5) { double ad = Angle.Separation(c.Horizontal, map.Center); if ((!isGround || c.Horizontal.Altitude + c.Semidiameter / 3600 > 0) && ad < map.ViewAngle + c.Semidiameter / 3600) { PointF p = map.Project(c.Horizontal); PointF t = map.Project(c.TailHorizontal); double tail = map.DistanceBetweenPoints(p, t); if (diam > 5 || tail > 10) { using (var gpComet = new GraphicsPath()) { double rotation = Math.Atan2(t.Y - p.Y, t.X - p.X) + Math.PI / 2; gpComet.StartFigure(); // tail is long enough if (tail > diam) { gpComet.AddArc(p.X - diam / 2, p.Y - diam / 2, diam, diam, (float)Angle.ToDegrees(rotation), 180); gpComet.AddLines(new PointF[] { gpComet.PathPoints[gpComet.PathPoints.Length - 1], t, gpComet.PathPoints[0] }); } // draw coma only else { gpComet.AddEllipse(p.X - diam / 2, p.Y - diam / 2, diam, diam); } gpComet.CloseAllFigures(); using (var brushComet = new PathGradientBrush(gpComet)) { brushComet.CenterPoint = p; int alpha = 100; if (c.Magnitude >= map.MagLimit) { alpha -= (int)(100 * (c.Magnitude - map.MagLimit) / c.Magnitude); } brushComet.CenterColor = map.GetColor(Color.FromArgb(alpha, colorComet)); brushComet.SurroundColors = gpComet.PathPoints.Select(pp => Color.Transparent).ToArray(); g.FillPath(brushComet, gpComet); } } if (drawLabels) { var font = settings.Get <Font>("CometsLabelsFont"); map.DrawObjectCaption(font, brushNames, c.Name, p, diam); } map.AddDrawnObject(c); } } } } } }
public override void Render(IMapContext map) { Graphics g = map.Graphics; var allComets = cometsCalc.Comets; bool isGround = settings.Get("Ground"); bool useTextures = settings.Get("PlanetsTextures"); bool drawComets = settings.Get("Comets"); bool drawLabels = settings.Get("CometsLabels"); bool drawAll = settings.Get <bool>("CometsDrawAll"); decimal drawAllMagLimit = settings.Get <decimal>("CometsDrawAllMagLimit"); bool drawLabelMag = settings.Get <bool>("CometsLabelsMag"); Brush brushNames = new SolidBrush(map.GetColor("ColorCometsLabels")); var font = settings.Get <Font>("CometsLabelsFont"); if (drawComets) { var comets = allComets.Where(a => Angle.Separation(map.Center, a.Horizontal) < map.ViewAngle); foreach (var c in comets) { float diam = map.GetDiskSize(c.Semidiameter); float size = map.GetPointSize(c.Magnitude); // if "draw all" setting is enabled, draw comets brighter than limit if (drawAll && size < 1 && c.Magnitude <= (float)drawAllMagLimit) { size = 1; } string label = drawLabelMag ? $"{c.Name} {Formatters.Magnitude.Format(c.Magnitude)}" : c.Name; if (diam > 5) { double ad = Angle.Separation(c.Horizontal, map.Center); if ((!isGround || c.Horizontal.Altitude + c.Semidiameter / 3600 > 0) && ad < map.ViewAngle + c.Semidiameter / 3600) { PointF p = map.Project(c.Horizontal); PointF t = map.Project(c.TailHorizontal); double tail = map.DistanceBetweenPoints(p, t); if (diam > 5 || tail > 10) { using (var gpComet = new GraphicsPath()) { double rotation = Math.Atan2(t.Y - p.Y, t.X - p.X) + Math.PI / 2; gpComet.StartFigure(); // tail is long enough if (tail > diam) { gpComet.AddArc(p.X - diam / 2, p.Y - diam / 2, diam, diam, (float)Angle.ToDegrees(rotation), 180); gpComet.AddLines(new PointF[] { gpComet.PathPoints[gpComet.PathPoints.Length - 1], t, gpComet.PathPoints[0] }); } // draw coma only else { gpComet.AddEllipse(p.X - diam / 2, p.Y - diam / 2, diam, diam); } gpComet.CloseAllFigures(); using (var brushComet = new PathGradientBrush(gpComet)) { brushComet.CenterPoint = p; int alpha = 100; if (c.Magnitude >= map.MagLimit) { alpha -= (int)(100 * (c.Magnitude - map.MagLimit) / c.Magnitude); } brushComet.CenterColor = map.GetColor(Color.FromArgb(alpha, colorComet)); brushComet.SurroundColors = gpComet.PathPoints.Select(pp => Color.Transparent).ToArray(); g.FillPath(brushComet, gpComet); } } if (drawLabels) { map.DrawObjectCaption(font, brushNames, label, p, diam); } map.AddDrawnObject(c); } } } else if ((int)size > 0) { PointF p = map.Project(c.Horizontal); if (!map.IsOutOfScreen(p)) { g.FillEllipse(new SolidBrush(map.GetColor(Color.White)), p.X - size / 2, p.Y - size / 2, size, size); if (drawLabels) { map.DrawObjectCaption(font, brushNames, label, p, size); } map.AddDrawnObject(c); continue; } } } } }
private void DrawGroupOfPoints(IMapContext map, Pen penGrid, PointF[] points, PointF[] refPoints) { // Do not draw figure containing less than 2 points if (points.Length < 2) { return; } // Two points can be simply drawn as a line if (points.Length == 2) { map.Graphics.DrawLine(penGrid, points[0], points[1]); return; } // Coordinates of the screen center var origin = new PointF(map.Width / 2, map.Height / 2); // Small radius is a screen diagonal double r = Math.Sqrt(map.Width * map.Width + map.Height * map.Height) / 2; // From 3 to 5 points. Probably we can straighten curve to line. // Apply some calculations to detect conditions when it's possible. if (points.Length > 2 && points.Length < 6) { // Determine start, middle and end points of the curve PointF pStart = points[0]; PointF pMid = points[points.Length / 2]; PointF pEnd = points[points.Length - 1]; // Get angle between middle and last points of the curve double alpha = map.AngleBetweenVectors(pMid, pStart, pEnd); double d1 = map.DistanceBetweenPoints(pStart, origin); double d2 = map.DistanceBetweenPoints(pEnd, origin); // It's almost a straight line if (alpha > 179) { // Check the at least one last point of the curve // is far enough from the screen center if (d1 > r * 2 || d2 > r * 2) { map.Graphics.DrawLine(penGrid, refPoints[0], refPoints[1]); return; } } // If both of last points of the line are far enough from the screen center // then assume that the curve is an arc of a big circle. // Check the curvature of that circle by comparing its radius with small radius if (d1 > r * 2 && d2 > r * 2) { var R = FindCircleRadius(points); if (R / r > 60) { map.Graphics.DrawLine(penGrid, refPoints[0], refPoints[1]); return; } } } if (points.All(p => map.DistanceBetweenPoints(p, origin) < r * 60)) { // Draw the curve in regular way map.Graphics.DrawCurve(penGrid, points); } }
private void RenderJupiterMoons(IMapContext map, Planet jupiter, IEnumerable <JupiterMoon> moons) { bool isGround = settings.Get <bool>("Ground"); bool useTextures = settings.Get <bool>("UseTextures"); double coeff = map.DiagonalCoefficient(); foreach (var moon in moons) { double ad = Angle.Separation(moon.Horizontal, map.Center); if ((!isGround || moon.Horizontal.Altitude + moon.Semidiameter / 3600 > 0) && ad < coeff * map.ViewAngle + moon.Semidiameter / 3600) { PointF p = map.Project(moon.Horizontal); PointF pJupiter = map.Project(jupiter.Horizontal); float size = map.GetPointSize(moon.Magnitude, 2); float diam = map.GetDiskSize(moon.Semidiameter); // diameter is to small to render moon disk, // but point size caclulated from magnitude is enough to be drawn if (size > diam && (int)size > 0) { // do not draw moon point if eclipsed if (!moon.IsEclipsedByJupiter) { // satellite is distant enough from the Jupiter // but too small to be drawn as disk if (map.DistanceBetweenPoints(p, pJupiter) >= 5) { map.Graphics.TranslateTransform(p.X, p.Y); map.Graphics.FillEllipse(new SolidBrush(map.GetColor(Color.Wheat)), -size / 2, -size / 2, size, size); map.Graphics.ResetTransform(); map.DrawObjectCaption(fontLabel, brushLabel, moon.Name, p, 2); map.AddDrawnObject(moon); } } } // moon should be rendered as disk else if (diam >= size && (int)diam > 0) { float rotation = map.GetRotationTowardsNorth(jupiter.Equatorial) + 360 - (float)jupiter.Appearance.P; map.Graphics.TranslateTransform(p.X, p.Y); map.Graphics.RotateTransform(rotation); if (useTextures) { Image texture = imagesCache.RequestImage($"5-{moon.Number}", new PlanetTextureToken($"5-{moon.Number}", moon.CM, jupiter.Appearance.D), PlanetTextureProvider, map.Redraw); if (texture != null) { map.DrawImage(texture, -diam / 2 * 1.01f, -diam / 2 * 1.01f, diam * 1.01f, diam * 1.01f); DrawVolume(map, diam, 0); } else { map.Graphics.FillEllipse(new SolidBrush(map.GetColor(Color.Wheat)), -diam / 2, -diam / 2, diam, diam); } } else { map.Graphics.FillEllipse(new SolidBrush(map.GetColor(Color.Wheat)), -diam / 2, -diam / 2, diam, diam); if (diam > 2) { map.Graphics.DrawEllipse(new Pen(map.GetSkyColor()), -diam / 2, -diam / 2, diam, diam); } } map.Graphics.ResetTransform(); // render another moon shadows on the moon RenderJupiterMoonShadow(map, moon, moon.RectangularS); // render Jupiter shadow on the moon if (moon.IsEclipsedByJupiter) { RenderJupiterShadow(map, moon); } map.DrawObjectCaption(fontLabel, brushLabel, moon.Name, p, diam); map.AddDrawnObject(moon); } } } }
private void DrawTrackSegment(IMapContext map, Pen penGrid, PointF[] points, PointF pBody, int iterationStep = 0) { iterationStep++; // Do not draw figure containing less than 2 points if (points.Length < 2) { return; } // Two points can be simply drawn as a line else if (points.Length == 2) { points = ShiftToAncorPoint(map, points, pBody); map.Graphics.DrawLine(penGrid, points[0], points[1]); } // interpolation is needed else if (points.Length > 2 && points.Length < 20 && iterationStep < 10) { // Coordinates of the screen center var origin = new PointF(map.Width / 2, map.Height / 2); // Screen diagonal double diag = Math.Sqrt(map.Width * map.Width + map.Height * map.Height); float maxX = points.Select(p => Math.Abs(p.X)).Max(); float maxY = points.Select(p => Math.Abs(p.Y)).Max(); float f = (float)diag / Math.Max(maxX, maxY); var scaledPoints = points.Select(p => new PointF(p.X * f, p.Y * f)).ToArray(); using (GraphicsPath gp = new GraphicsPath()) { gp.AddCurve(scaledPoints); gp.Flatten(); scaledPoints = gp.PathPoints.Select(p => new PointF(p.X / f, p.Y / f)).ToArray(); var segments = scaledPoints.Select(p => map.DistanceBetweenPoints(p, origin) < diag * 3 ? p : PointF.Empty) .Split(p => p == PointF.Empty, true); foreach (var segment in segments) { var p1 = scaledPoints.Prev(segment.First()); var p2 = scaledPoints.Next(segment.Last()); List <PointF> newPoints = new List <PointF>(segment); if (p1 != PointF.Empty) { newPoints.Insert(0, p1); } if (p2 != PointF.Empty) { newPoints.Add(p2); } DrawTrackSegment(map, penGrid, newPoints.ToArray(), pBody, iterationStep); } if (!segments.Any()) { var p0 = scaledPoints.OrderBy(p => map.DistanceBetweenPoints(p, origin)).First(); var p1 = scaledPoints.Prev(p0); var p2 = scaledPoints.Next(p0); List <PointF> newPoints = new List <PointF>(); if (p1 != PointF.Empty) { newPoints.Insert(0, p1); } if (p2 != PointF.Empty) { newPoints.Add(p2); } DrawTrackSegment(map, penGrid, newPoints.ToArray(), pBody, iterationStep); } } } // draw the curve in regular way else { map.Graphics.DrawCurve(penGrid, ShiftToAncorPoint(map, points, pBody)); } }