public static ILength GetDistance(ILineSegmentGeometry g, IPosition point) { double xp, yp; BasicGeom.GetPerpendicular(point.X, point.Y, g.Start.X, g.Start.Y, g.End.X, g.End.Y, out xp, out yp); double d = BasicGeom.Distance(new Position(xp, yp), point); return(new Length(d)); }
static double MinDistanceSquared(ICircularArcGeometry g, IPosition p) { // If the position lies in the arc sector, the minimum distance // is given by the distance to the circle. Otherwise the minimum // distance is the distance to the closest end of the arc. if (IsInSector(g, p, 0.0)) { double dist = BasicGeom.Distance(p, g.Circle.Center); double radius = g.Circle.Radius; return((dist - radius) * (dist - radius)); } else { double d1 = BasicGeom.DistanceSquared(p, g.BC); double d2 = BasicGeom.DistanceSquared(p, g.EC); return(Math.Min(d1, d2)); } }
/// <summary> /// Gets geometric info for this geometry. For use during the formation /// of <c>Polygon</c> objects. /// </summary> /// <param name="window">The window of the geometry</param> /// <param name="area">The area (in square meters) between the geometry and the Y-axis.</param> /// <param name="length">The length of the geometry (in meters on the (projected) ground).</param> internal override void GetGeometry(out IWindow win, out double area, out double length) { IPosition start = Start; IPosition end = End; // Define the window. win = new Window(start, end); // Define line length length = BasicGeom.Distance(start, end); // Define area to the left of the line. // Uses the mid-X of the segment to get the area left (signed). // If the line is directed up the way, it contributes a negative // area. If directed down, it contributes a positive area. So, // if flat, it contributes nothing. double dy = start.Y - end.Y; area = dy * 0.5 * (start.X + end.X); }
public static bool GetPosition(IPosition start, IPosition end, double d, out IPosition result) { const double TOL = 0.000002; // Check for distance that is real close to the start (less // than 1 micron or so, since that's the smallest number we // can store). if (d < TOL) { result = start; return(d >= 0.0); } // Get length of the line double len = BasicGeom.Distance(start, end); // If the required distance is within the limiting tolerance // of the end of the line (or beyond it), return the end. if (d > len || (len - d) < TOL) { result = end; return(d <= (len + TOL)); } // How far up the line do we need to go? double ratio = d / len; // Figure out the position double x1 = start.X; double y1 = start.Y; double x2 = end.X; double y2 = end.Y; double dx = x2 - x1; double dy = y2 - y1; result = new Position(x1 + ratio * dx, y1 + ratio * dy); return(true); }
public static ILength Distance(ICircleGeometry g, IPosition p) { double d = BasicGeom.Distance(g.Center, p); return(new Length(Math.Abs(d - g.Radius))); }
private ArcFeature ImportArc(Ntx.Line line, Operation creator, ILength tol) { Debug.Assert(line.IsCurve); IEntity what = GetEntityType(line, SpatialType.Line); // Get positions defining the arc PointGeometry[] pts = GetPositions(line); // Ignore zero-length lines if (HasZeroLength(pts)) { return(null); } // Add a point at the center of the circle Ntx.Position pos = line.Center; PointGeometry pc = new PointGeometry(pos.Easting, pos.Northing); PointFeature center = EnsurePointExists(pc, tol, creator); // Calculate exact positions for the arc endpoints double radius = line.Radius; ICircleGeometry cg = new CircleGeometry(pc, radius); IPosition bc = CircleGeometry.GetClosestPosition(cg, pts[0]); IPosition ec = CircleGeometry.GetClosestPosition(cg, pts[pts.Length - 1]); // Round off to nearest micron PointGeometry bcg = PointGeometry.Create(bc); PointGeometry ecg = PointGeometry.Create(ec); // Ensure point features exist at both ends of the line. PointFeature ps = GetArcEndPoint(bcg, tol, creator); PointFeature pe = GetArcEndPoint(ecg, tol, creator); // Try to find a circle that's already been added by this import. Circle c = EnsureCircleExists(center, radius, tol, creator); // Determine which way the arc is directed bool iscw = LineStringGeometry.IsClockwise(pts, center); InternalIdValue id = CadastralMapModel.Current.WorkingSession.AllocateNextId(); ArcFeature arc = new ArcFeature(creator, id, what, c, ps, pe, iscw); // The toological status of the incoming arc may override the status that the // constructor derived from the entity type arc.SetTopology(line.IsTopologicalArc); #if DEBUG // Confirm the NTX data was valid (ensure it's consistent with what we've imported)... double readRad = c.Radius; double calcRad = BasicGeom.Distance(c.Center, ps); Debug.Assert(Math.Abs(readRad - calcRad) < tol.Meters); foreach (IPointGeometry pg in pts) { ILength check = arc.Geometry.Distance(pg); Debug.Assert(check.Meters < tol.Meters); } #endif return(arc); }
/// <summary> /// Obtains the geometry for spans along this leg. /// </summary> /// <param name="bc">The position for the start of the leg. /// <param name="bcBearing">The bearing on entry into the leg.</param> /// <param name="sfac">Scale factor to apply to distances.</param> /// <param name="spans">Information for the spans coinciding with this leg.</param> /// <returns>The sections along this leg</returns> internal override ILineGeometry[] GetSpanSections(IPosition bc, double bcBearing, double sfac, SpanInfo[] spans) { // Can't do anything if the leg radius isn't defined if (m_Metrics.ObservedRadius == null) { throw new InvalidOperationException("Cannot create sections for circular leg with undefined radius"); } var result = new ILineGeometry[spans.Length]; // Use supplied stuff to derive info on the center and EC. IPosition center; IPosition ec; double bearingToBC; double ecBearing; GetPositions(bc, bcBearing, sfac, out center, out bearingToBC, out ec, out ecBearing); // Define the underlying circle ICircleGeometry circle = new CircleGeometry(PointGeometry.Create(center), BasicGeom.Distance(center, bc)); // Handle case where the leg is a cul-de-sac with no observed spans if (spans.Length == 1 && spans[0].ObservedDistance == null) { result[0] = new CircularArcGeometry(circle, bc, ec, m_Metrics.IsClockwise); return(result); } /// Initialize scaling factor for distances in cul-de-sacs (ratio of the length calculated from /// the CA & Radius, versus the observed distances that were actually specified). For curves that /// are not cul-de-sacs, this will be 1.0 double culFactor = 1.0; if (m_Metrics.IsCulDeSac) { double obsv = PrimaryFace.GetTotal(); if (obsv > MathConstants.TINY) { culFactor = Length.Meters / obsv; } } IPosition sPos = bc; IPosition ePos = null; bool isClockwise = m_Metrics.IsClockwise; double radius = RadiusInMeters; double edist = 0.0; for (int i = 0; i < result.Length; i++, sPos = ePos) { // Add on the unscaled distance edist += spans[i].ObservedDistance.Meters; // Get the angle subtended at the center of the circle. We use // unscaled values here, since the scale factors would cancel out. // However, we DO apply any cul-de-sac scaling factor, to account // for the fact that the distance may be inconsistent with the // curve length derived from the CA and radius. For example, it // is possible that the calculated curve length=200, although the // total of the observed spans is somehow only 100. In that case, // if the supplied distance is 50, we actually want to use a // value of 50 * (200/100) = 100. double angle = (edist * culFactor) / radius; // Get the bearing of the point with respect to the center of the circle. double bearing; if (isClockwise) { bearing = bearingToBC + angle; } else { bearing = bearingToBC - angle; } // Calculate the position using the scaled radius. ePos = Geom.Polar(center, bearing, radius * sfac); result[i] = new CircularArcGeometry(circle, sPos, ePos, isClockwise); } return(result); }
public ILength Distance(IPosition point) { return(new Length(BasicGeom.Distance(this, point))); }
/// <summary> /// Intersects a line segment with a circle. /// </summary> /// <param name="centre">Position of the circle's centre.</param> /// <param name="radius">Radius of the circle.</param> /// <param name="start">Start of segment.</param> /// <param name="end">End of segment.</param> /// <param name="x1">First intersection (if any).</param> /// <param name="x2">Second intersection (if any).</param> /// <param name="istangent">Is segment a tangent to the circle? This can be true only if /// ONE intersection is returned.</param> /// <param name="online">TRUE if the intersection must lie ON the segment.</param> /// <returns>The number of intersections found (0, 1, or 2).</returns> internal static uint IntersectCircle(IPosition centre , double radius , IPosition start , IPosition end , out IPosition ox1 , out IPosition ox2 , out bool istangent , bool online) { // Initialize the return info. Position x1 = new Position(0, 0); Position x2 = new Position(0, 0); ox1 = x1; ox2 = x2; istangent = false; // Get the bearing of this segment. double bearing = Geom.BearingInRadians(start, end); // The equation of each line is given in parametric form as: // // x = xo + f * r // y = yo + g * r // // where xo,yo is the from point // and f = sin(bearing) // and g = cos(bearing) // and r is the distance along the line. double g = Math.Cos(bearing); double f = Math.Sin(bearing); double fsq = f * f; double gsq = g * g; double startx = start.X; double starty = start.Y; double dx = centre.X - startx; double dy = centre.Y - starty; double fygx = f * dy - g * dx; double root = radius * radius - fygx * fygx; // Check for no intersection. if (root < -Constants.TINY) { return(0); } // We've either got 1 or 2 intersections ... // If the intersection has to be ON the line, we'll need // the length of the line segment. double seglen = 0.0; if (online) { seglen = BasicGeom.Distance(start, end); } // Check for tangential intersection. if (root < Constants.TINY) { double xdist = f * dx + g * dy; if (online && (xdist < 0.0 || xdist > seglen)) { return(0); } x1.X = startx + f * xdist; x1.Y = starty + g * xdist; istangent = true; return(1); } // That leaves us with 2 intersections, although one or both of // them may not actually fall on the segment. double fxgy = f * dx + g * dy; root = Math.Sqrt(root); double dist1 = (fxgy - root); double dist2 = (fxgy + root); if (online) { uint nok = 0; if (dist1 > 0.0 && dist1 < seglen) { x1.X = startx + f * dist1; x1.Y = starty + g * dist1; nok = 1; } if (dist2 > 0.0 && dist2 < seglen) { if (nok == 0) { x1.X = startx + f * dist2; x1.Y = starty + g * dist2; return(1); } x2.X = startx + f * dist2; x2.Y = starty + g * dist2; return(2); } return(nok); } // Doesn't need to be ON the segment. x1.X = startx + f * dist1; x1.Y = starty + g * dist1; x2.X = startx + f * dist2; x2.Y = starty + g * dist2; return(2); }
/// <summary> /// Creates a new <c>CircularArcGeometry</c> where the radius of the circle is defined as /// distance from the BC to the supplied circle center. /// </summary> /// <param name="center">The center of the circle</param> /// <param name="bc">The start of the arc (the circle passes through this point)</param> /// <param name="ec">The end of the arc (assumed to coincide with the circle)</param> /// <param name="isClockwise">Is the arc directed clockwise?</param> public CircularArcGeometry(IPointGeometry center, IPosition bc, IPosition ec, bool isClockwise) : this(new CircleGeometry(center, BasicGeom.Distance(center, bc)), bc, ec, isClockwise) { }
public static ILength GetLength(ILineSegmentGeometry g) { double d = BasicGeom.Distance(g.Start, g.End); return(new Length(d)); }
/// <summary> /// Draws a text string (annotation) /// </summary> /// <param name="display">The display to draw to</param> /// <param name="text">The item of text</param> public void Render(ISpatialDisplay display, IString text) { // Draw the outline if it's too small Font f = text.CreateFont(display); IPosition[] outline = text.Outline; if (outline == null) { // This is a bit of a hack that covers the Backsight.Editor.Annotation class... if (f == null) { return; } // Note that the order you apply the transforms is significant... IPointGeometry pg = text.Position; PointF p = CreatePoint(display, pg); display.Graphics.TranslateTransform(p.X, p.Y); double rotation = text.Rotation.Degrees; display.Graphics.RotateTransform((float)rotation); StringFormat sf = text.Format; if (sf == null) { sf = StringFormat.GenericTypographic; } string s = text.Text; display.Graphics.DrawString(s, f, Brush, 0, 0, sf); display.Graphics.ResetTransform(); // TEST -- draw rotated 180 to see if that's all we need to do flip... looks good /* * display.Graphics.TranslateTransform(p.X, p.Y); * display.Graphics.RotateTransform((float)rotation+180); * display.Graphics.DrawString(s, f, Brush, 0, 0, sf); * display.Graphics.ResetTransform(); */ } else { if (f == null) { Render(display, outline); } else { string s = text.Text; // Note that the order you apply the transforms is significant... PointF p = CreatePoint(display, outline[0]); display.Graphics.TranslateTransform(p.X, p.Y); double rotation = text.Rotation.Degrees; display.Graphics.RotateTransform((float)rotation); Size size = TextRenderer.MeasureText(s, f); double groundWidth = BasicGeom.Distance(outline[0], outline[1]); float xScale = display.LengthToDisplay(groundWidth) / (float)size.Width; float yScale = f.Size / (float)size.Height; // ScaleTransform doesn't like values of 0 (single character names lead to outline with no width, // should really see if proper character width can be determined in that case). if (xScale < Single.Epsilon) { xScale = yScale; } display.Graphics.ScaleTransform(xScale, yScale); // I tried StringFormat.GenericDefault, but that seems to leave too much // leading space. display.Graphics.DrawString(s, f, Brush, 0, 0, StringFormat.GenericTypographic); display.Graphics.ResetTransform(); } } }