/// <summary> /// Creates new <c>LineStringGeometry</c> that corresponds to the supplied positions. /// </summary> /// <param name="data">The positions defining the line.</param> public LineStringGeometry(IPointGeometry[] data) { if (data==null || data.Length<2) throw new ArgumentException(); m_Data = data; }
/// <summary> /// Gets the point on this line that is closest to a specified position. /// </summary> /// <param name="p">The position to search from.</param> /// <param name="tol">Maximum distance from line to the search position</param> /// <returns>The closest position (null if the line is further away than the specified /// max distance)</returns> internal override IPosition GetClosest(IPointGeometry p, ILength tol) { // Get the perpendicular point (or closest end) IPointGeometry s = Start; IPointGeometry e = End; double xp, yp; BasicGeom.GetPerpendicular(p.X, p.Y, s.X, s.Y, e.X, e.Y, out xp, out yp); // Ignore if point is too far away double t = tol.Meters; double dx = p.X - xp; if (dx > t) { return(null); } double dy = p.Y - yp; if (dy > t) { return(null); } double dsq = (dx * dx + dy * dy); if (dsq > t * t) { return(null); } return(new Position(xp, yp)); }
/// <summary> /// Creates a new <c>FindClosestQuery</c> (and executes it). The result of the query /// can then be obtained through the <c>Result</c> property. /// </summary> /// <param name="index">The spatial index to search</param> /// <param name="p">The search position.</param> /// <param name="radius">The search tolerance (expected to be greater than zero).</param> /// <param name="types">The type of objects to look for.</param> internal FindClosestQuery(ISpatialIndex index, IPosition p, ILength radius, SpatialType types) { if (types == 0) { throw new ArgumentNullException("Spatial type(s) not specified"); } // If the user hasn't been specific, ensure we don't search for polygons! SpatialType useTypes = (types == SpatialType.All ? SpatialType.Feature : types); Debug.Assert((useTypes & SpatialType.Polygon) == 0); // It's important to round off to the nearest micron. Otherwise you might not // get the desired results in situations where the search radius is zero. m_Position = PositionGeometry.Create(p); m_Radius = radius; m_Types = types; m_Result = null; m_Distance = m_Radius.Meters; // The query will actually involve a square window, not a circle. IWindow x = new Window(m_Position, radius.Meters * 2.0); index.QueryWindow(x, useTypes, OnQueryHit); }
/// <summary> /// Delegate that's called whenever the index finds text with a window that overlaps the query window /// </summary> /// <param name="item">The item to process (expected to be some sort of <c>TextFeature</c>)</param> /// <returns>True (always), indicating that the query should continue.</returns> private bool OnTextFound(ISpatialObject item) { // Ignore test that's already been built TextFeature text = (TextFeature)item; if (text.IsBuilt) { return(true); } if (text.IsTopological) { // Get the label's reference position. IPointGeometry posn = text.GetPolPosition(); // Try to find enclosing polygon if (m_Polygon.IsEnclosing(posn)) { m_Result = text; return(false); } } return(true); }
/// <summary> /// Constructor /// </summary> /// <param name="cc">Object for holding any displayed dialogs</param> /// <param name="action">The action that initiated this command</param> /// <param name="start">Point initially selected at start of command</param> internal NewLineUI(IControlContainer cc, IUserAction action, PointFeature start) : base(cc, action) { m_Start = start; m_End = null; m_CurrentPoint = start; }
/// <summary> /// Checks whether this closed shape overlaps a line /// </summary> /// <param name="shape">Positions defining a closed outline (the last position /// must coincide with the first position). At least 3 positions.</param> /// <param name="shapeWindow">The extent of <paramref name="shape"/></param> /// <param name="line">The line to compare with the shape</param> /// <returns>True if the shape overlaps the line</returns> internal bool IsOverlap(LineGeometry line) { // Get the window of the line IWindow lwin = line.Extent; // The two windows have to overlap. if (!m_Extent.IsOverlap(lwin)) { return(false); } // If either end of the line falls inside this shape, we've got overlap. IPointGeometry p = line.Start; if (m_Extent.IsOverlap(p) && Geom.IsPointInClosedShape(this.Data, p)) { return(true); } p = line.End; if (m_Extent.IsOverlap(p) && Geom.IsPointInClosedShape(this.Data, p)) { return(true); } // The only thing left is a possible interesection between // the shape and the line. return(IsIntersect(line)); }
/// <summary> /// Checks whether a location falls in the sector lying between the BC and EC /// of a circular arc. The only condition is that the location has an appropriate /// bearing with respect to the bearings of the BC & EC (i.e. the location does not /// necessarily lie ON the arc). /// </summary> /// <param name="pos">The position to check</param> /// <param name="lintol">Linear tolerance, in meters on the ground (locations that are /// beyond the BC or EC by up to this much will also be regarded as "in-sector").</param> /// <returns>True if position falls in the sector defined by this arc</returns> public static bool IsInSector(ICircularArcGeometry g, IPosition pos, double lintol) { // If the arc is a complete circle, it's ALWAYS in sector. if (IsCircle(g)) { return(true); } // Express the curve's start & end points in local system, // ordered clockwise. IPointGeometry start = g.First; IPointGeometry end = g.Second; // Get the centre of the circular arc. IPosition centre = g.Circle.Center; // If we have a linear tolerance, figure out the angular equivalent. if (lintol > MathConstants.TINY) { double angtol = lintol / g.Circle.Radius; return(BasicGeom.IsInSector(pos, centre, start, end, angtol)); } else { return(BasicGeom.IsInSector(pos, centre, start, end, 0.0)); } }
/// <summary> /// Creates a new <c>FindPointContainerQuery</c> (and executes it). The result of the query /// can then be obtained through the <c>Result</c> property. /// </summary> /// <param name="index">The spatial index to search</param> /// <param name="point">The position you want the container for</param> internal FindPointContainerQuery(ISpatialIndex index, IPointGeometry p) { m_Point = p; m_Result = null; IWindow w = new Window(p, p); index.QueryWindow(w, SpatialType.Polygon, OnQueryHit); // If we didn't get a result, but we skipped some candidates, check them now. if (m_Result == null && m_Candidates != null) { // If NONE of the polygon's islands enclose the search position, that's // the result we want. foreach (Polygon cand in m_Candidates) { Debug.Assert(cand.HasAnyIslands); if (!cand.HasIslandEnclosing(m_Point)) { m_Result = cand; return; } } } }
/// <summary> /// Creates a new <c>FindPointContainerQuery</c> (and executes it). The result of the query /// can then be obtained through the <c>Result</c> property. /// </summary> /// <param name="index">The spatial index to search</param> /// <param name="point">The position you want the container for</param> internal FindPointContainerQuery(ISpatialIndex index, IPointGeometry p) { m_Point = p; m_Result = null; IWindow w = new Window(p, p); index.QueryWindow(w, SpatialType.Polygon, OnQueryHit); // If we didn't get a result, but we skipped some candidates, check them now. if (m_Result==null && m_Candidates!=null) { // If NONE of the polygon's islands enclose the search position, that's // the result we want. foreach (Polygon cand in m_Candidates) { Debug.Assert(cand.HasAnyIslands); if (!cand.HasIslandEnclosing(m_Point)) { m_Result = cand; return; } } } }
public CircularArcGeometry(ICircleGeometry circle, IPointGeometry bc, IPointGeometry ec, bool isClockwise) { m_Circle = circle; m_BC = bc; m_EC = ec; m_IsClockwise = isClockwise; }
void WriteArc(ArcGeometry line) { IPointGeometry center = line.Circle.Center; double bcAngle = GetAngle(center, line.BC); double ecAngle = GetAngle(center, line.EC); Arc acLine = new Arc(); acLine.Center = new Vector3d(center.X, center.Y, 0.0); acLine.Radius = line.Circle.Radius; // AutoCad arcs are *always* drawn counter-clockwise if (line.IsClockwise) { acLine.StartAngle = ecAngle; acLine.EndAngle = bcAngle; } else { acLine.StartAngle = bcAngle; acLine.EndAngle = ecAngle; } acLine.Layer = m_Layer; m_Dxf.AddEntity(acLine); }
void ExportKey(TextFeature text) { Layer layer = m_Layers.GetLayer(text, m_ContinuousLineType, true); if (layer == null) { return; } // Get the label's key string. It HAS to be defined. string keystr = text.FormattedKey; if (String.IsNullOrEmpty(keystr)) { return; } // Get the label's reference position. IPointGeometry refpos = text.GetPolPosition(); // Define the position for AutoCad (bottom right corner). TextGeometry geom = text.TextGeometry; Text acText = new Text(geom.Text, GetVector(geom.Position), geom.Height); acText.Rotation = (float)geom.Rotation.Degrees; acText.Layer = layer; m_Dxf.AddEntity(acText); }
/// <summary> /// Checks whether any intersections occur at positions that do not coincide /// with the end points of a line. /// </summary> /// <param name="line">The line to check. This should be the same line that /// was supplied to the <c>IntersectionFinder</c> constructor that created THIS results /// object (doing otherwise doesn't make any sense).</param> /// <returns>TRUE if any intersection does not coincide with the end points /// of the specified line.</returns> internal bool IsSplitOn(ILineGeometry line) { // Get the locations of the line end points. IPointGeometry start = line.Start; IPointGeometry end = line.End; // Go through each intersection, looking for one that does // not correspond to the line ends. foreach (IntersectionData d in m_Data) { IPointGeometry loc1 = new PointGeometry(d.P1); if (!start.IsCoincident(loc1) && !end.IsCoincident(loc1)) { return(true); } if (d.IsGraze) { /* * Huh? This was the original, but it always ended up returning true. * * IPointGeometry loc2 = PointGeometry.New(d.P2); * if (!start.IsCoincident(loc2) && !end.IsCoincident(loc2)) * return true; * * return true; */ return(true); } } return(false); }
/// <summary> /// Adds a termination point (or re-use a point if it happens to be the offset point). /// </summary> /// <param name="loc">The location for the termination point.</param> /// <returns>The point feature at the termination point (may be the position that was /// used to define the offset to the parallel line).</returns> PointFeature AddPoint(IPosition loc) { CadastralMapModel map = CadastralMapModel.Current; // Add the split point (with default entity type). If a // point already exists at the location, you'll get back // that point instead. // We do this in case the user has decided to // terminate on a line connected to the offset point // that defines the offset to the parallel (which the // UI lets the user do). PointFeature p = this.OffsetPoint; if (p != null) { IPointGeometry pg = PointGeometry.Create(loc); if (p.IsCoincident(pg)) { return(p); } } p = map.AddPoint(p, map.DefaultPointType, this); p.SetNextId(); return(p); }
public CircularArcGeometry(ICircleGeometry circle, IPosition bc, IPosition ec, bool isClockwise) { m_Circle = circle; m_BC = PositionGeometry.Create(bc); m_EC = PositionGeometry.Create(ec); m_IsClockwise = isClockwise; }
/// <summary> /// Writes the content of this instance to a persistent storage area. /// </summary> /// <param name="editSerializer">The mechanism for storing content.</param> public override void WriteData(EditSerializer editSerializer) { base.WriteData(editSerializer); editSerializer.WriteBool(DataField.Topological, IsTopological); IPointGeometry tp = Position; IPointGeometry pp = GetPolPosition(); if (pp != null) { if (pp.Easting.Microns != tp.Easting.Microns || pp.Northing.Microns != tp.Northing.Microns) { editSerializer.WriteInt64(DataField.PolygonX, pp.Easting.Microns); editSerializer.WriteInt64(DataField.PolygonY, pp.Northing.Microns); } } // RowText is problematic on deserialization because the database rows might not // be there. To cover that possibility, use a proxy object. if (m_Geom is RowTextGeometry) { editSerializer.WritePersistent <TextGeometry>(DataField.Type, new RowTextContent((RowTextGeometry)m_Geom)); } else { editSerializer.WritePersistent <TextGeometry>(DataField.Type, m_Geom); } }
/// <summary> /// Checks an array of positions that purportedly correspond to a circular arc, to see /// whether the data is directed clockwise or not. No checks are made to confirm that /// the data really does correspond to a circular arc. /// </summary> /// <param name="pts">The positions defining an arc</param> /// <param name="center">The position of the centre of the circle that the arc lies on.</param> /// <returns>True if the data is ordered clockwise.</returns> public static bool IsClockwise(IPointGeometry[] pts, IPointGeometry center) { // To determine the direction, we must locate two successive // vertices that lie in the same quadrant (with respect to the // circle center). QuadVertex start = new QuadVertex(center, pts[0]); QuadVertex end = null; for (int i = 1; i < pts.Length; i++, start = end) { // Pick up the position at the end of the line segment. end = new QuadVertex(center, pts[i]); // If both ends of the segment are in the same quadrant, // see which one comes first. The result tells us whether // the curve is clockwise or not. if (start.Quadrant == end.Quadrant) { return(start.GetTanAngle() < end.GetTanAngle()); } } // Something has gone wrong if we got here! Debug.Assert(1 == 2); return(true); }
/// <summary> /// Creates a new <c>FindPointQuery</c> (and executes it). The result of the query /// can then be obtained through the <c>Result</c> property. /// </summary> /// <param name="index">The spatial index to search</param> /// <param name="point">The position of interest</param> internal FindPointQuery(ISpatialIndex index, IPointGeometry p) { m_Point = p; m_Result = null; IWindow w = new Window(p, p); index.QueryWindow(w, SpatialType.Point, OnQueryHit); }
/// <summary> /// Returns a code indicating the relative position of a location with respect to /// a rectangular window (the Cohen & Sutherland clipping algorithm). /// </summary> /// <param name="p">The position of interest</param> /// <param name="sw">South-west corner of window</param> /// <param name="ne">North-east corner of window</param> /// <returns></returns> internal static byte GetPositionCode(IPointGeometry p, IPointGeometry sw, IPointGeometry ne) { byte code = 0; long e = p.Easting.Microns; if (e < sw.Easting.Microns) // p west of sw { code |= 0x01; } else if (e > ne.Easting.Microns) // p east of ne { code |= 0x02; } long n = p.Northing.Microns; if (n < sw.Northing.Microns) // p south of sw { code |= 0x04; } else if (n > ne.Northing.Microns) // p north of ne { code |= 0x08; } return(code); }
public CircularArcGeometry(ICircularArcGeometry g) { m_Circle = g.Circle; m_BC = g.BC; m_EC = g.EC; m_IsClockwise = g.IsClockwise; }
/// <summary> /// Ensure that this label knows it's enclosing polygon. /// </summary> /// <returns>True if label successfully associated with polygon.</returns> internal void SetPolygon() { // Return if this label is already associated with a polygon (or the // label was previously found to be inside nothing) if (IsBuilt) { return; } // If this is a non-topological label, mark is as "built" // and return (it should have been previously marked as built). if (IsTopological) { // Get the label's reference position. IPointGeometry posn = GetPolPosition(); // Try to find enclosing polygon ISpatialIndex index = CadastralMapModel.Current.Index; Polygon enc = new FindPointContainerQuery(index, posn).Result; if (enc != null) { enc.ClaimLabel(this); } } SetBuilt(true); }
/// <summary> /// Defines orientation info. /// </summary> /// <param name="orient">The orientation position.</param> void SetOrient(IPosition orient) { if (m_Divider == null) { return; } // Get the position of the point that the divider meets. IPointGeometry loc = (m_IsStart ? m_Divider.From : m_Divider.To); // Figure out the deltas of the orientation point with respect to the point. double dx = orient.X - loc.X; double dy = orient.Y - loc.Y; // What quadrant are we in? m_Quadrant = WhatQuadrant(dx, dy); // Convert the deltas into an IJ coordinate system. When we // calculate I/J, it will always give us tan(angle) in each // successive quadrant, with the start of the quadrant resulting // in an angle of zero, and the end of the quadrant resulting in // an angle just less than infinity. switch (m_Quadrant) { case Quadrant.NE: { m_DeltaI = dx; m_DeltaJ = dy; return; } case Quadrant.SE: { m_DeltaI = -dy; m_DeltaJ = dx; return; } case Quadrant.SW: { m_DeltaI = -dx; m_DeltaJ = -dy; return; } case Quadrant.NW: { m_DeltaI = dy; m_DeltaJ = -dx; return; } } string msg = String.Format("Orientation.SetOrient - Quadrant error at {0:0.0000}N {1:0.0000}E", loc.Y, loc.X); throw new Exception(msg); }
/// <summary> /// Generates an approximation of a circular arc. /// </summary> /// <param name="tol">The maximum chord-to-circumference distance.</param> /// <returns></returns> public static IPointGeometry[] GetApproximation(ICircularArcGeometry g, ILength tol) { // Get info about the circle the curve lies on. IPosition center = g.Circle.Center; double radius = g.Circle.Radius; // Determine the change in bearing which will satisfy the specified tolerance // (if no tolerance has been specified, arbitrarily use a tolerance of 1mm on the ground). double tolm = (tol.Meters > Double.Epsilon ? tol.Meters : 0.001); double dbear = Math.Acos((radius - tolm) / radius); IPointGeometry start = g.BC; IPointGeometry end = g.EC; bool iscw = g.IsClockwise; // Get the total angle subtended by the curve. Turn reft = new Turn(center, start); double totang = reft.GetAngleInRadians(end); // clockwise if (!iscw) { totang = MathConstants.PIMUL2 - totang; } // Figure out how many positions we'll generate int nv = (int)(totang / dbear); // truncate Debug.Assert(nv >= 0); // Handle special case of very short arc. if (nv == 0) { return new IPointGeometry[] { start, end } } ; // Sign the delta-bearing the right way. if (!iscw) { dbear = -dbear; } // Get the initial bearing to the first position along the curve. double curbear = reft.BearingInRadians + dbear; // Append positions along the length of the curve. List <IPointGeometry> result = new List <IPointGeometry>(nv); result.Add(start); for (int i = 0; i < nv; i++, curbear += dbear) { IPosition p = BasicGeom.Polar(center, curbear, radius); result.Add(PositionGeometry.Create(p)); } result.Add(end); return(result.ToArray()); }
/// <summary> /// Initializes a new instance of the <see cref="Annotation"/> class with the /// <see cref="FontStyle"/> property set to <see cref="System.Drawing.FontStyle.Regular"/>. /// </summary> /// <param name="text">The annotation text.</param> /// <param name="position">The position for the text (center-baseline aligned).</param> /// <param name="height">The height of the text (in meters on the ground).</param> /// <param name="rotation">The rotation (in radians clockwise from horizontal).</param> internal Annotation(string text, IPosition position, double height, double rotation) { m_Text = text; m_Position = PointGeometry.Create(position); m_Height = height; m_Rotation = new RadianValue(rotation); m_FontStyle = FontStyle.Regular; }
internal HorizontalRay(IPosition start, double distance) { if (distance < 0.0) throw new ArgumentOutOfRangeException(); m_Start = PointGeometry.Create(start); m_EndX = start.X + distance; }
/// <summary> /// Cuts back a horizontal line segment to the closest intersection with this line. /// Used in point in polygon. /// </summary> /// <param name="s">Start of horizontal segment.</param> /// <param name="e">End of segment (will be modified if segment intersects this line)</param> /// <param name="status">Return code indicating whether an error has arisen (returned /// as 0 if no error).</param> /// <returns>True if the horizontal line was cut back.</returns> internal override bool GetCloser(IPointGeometry s, ref PointGeometry e, out uint status) { status = 0; // Remember the initial end of segment PointGeometry initEnd = new PointGeometry(e); // Represent the horizontal segment in a class of its own HorizontalRay hseg = new HorizontalRay(s, e.X - s.X); if (!hseg.IsValid) { status = 1; return(false); } IPointGeometry[] data = this.Data; // Get relative position code for the start of the line. If it's // somewhere on the horizontal segment, cut the line back. byte scode = Geom.GetPositionCode(data[0], s, e); if (scode == 0) { e = new PointGeometry(Start); } // Loop through each line segment, testing the end of each segment // against the horizontal segment. byte ecode; for (int i = 1; i < data.Length; scode = ecode, i++) { // Get the position code for the end of the line segment ecode = Geom.GetPositionCode(data[i], s, e); // If it's coincident with the horizontal segment, cut the // line back. Otherwise see whether there is any potential // intersection to cut back to. if (ecode == 0) { e = new PointGeometry(data[i]); } else if ((scode & ecode) == 0) { IPosition x = null; if (hseg.Intersect(data[i - 1], data[i], ref x)) { e = new PointGeometry(x); } } } // Return flag to indicate whether we got closer or not. return(!e.IsCoincident(initEnd)); }
internal HorizontalRay(IPosition start, double distance) { if (distance < 0.0) { throw new ArgumentOutOfRangeException(); } m_Start = PointGeometry.Create(start); m_EndX = start.X + distance; }
/// <summary> /// Sees whether the orientation of the new text should be altered. This /// occurs if the auto-angle capability is enabled, and the specified /// position is close to any visible line. /// </summary> /// <param name="refpos">The current mouse position (reference position /// for the new label)</param> /// <returns>True if the orientation angle got changed.</returns> bool CheckOrientation(IPointGeometry refpos) { // Return if the auto-angle function is disabled. if (!m_IsAutoAngle) { return(false); } // Return if the auto-position function is enabled if (m_IsAutoPos) { return(false); } // Try to get an orientation line LineFeature orient = GetOrientation(refpos); if (!Object.ReferenceEquals(orient, m_Orient)) { ErasePainting(); } m_Orient = null; if (orient == null) { return(false); } // Locate the closest point on the line (we SHOULD find it, // but if we don't, just bail out) ISpatialDisplay display = ActiveDisplay; ILength tol = new Length(0.002 * display.MapScale); IPosition closest = orient.LineGeometry.GetClosest(refpos, tol); if (closest == null) { return(false); } m_Orient = orient; if (m_Orient == null) { return(false); } // Highlight the new orientation line m_Orient.Render(display, new HighlightStyle()); // Get the rotation angle IPointGeometry cg = PointGeometry.Create(closest); double rot = m_Orient.LineGeometry.GetRotation(cg); SetRotation(rot); return(true); }
/// <summary> /// Handles mouse-move. /// </summary> /// <param name="pos">The new position of the mouse</param> internal override void MouseMove(IPosition pos) { // If we previously drew a text outline, erase it now. EraseRect(); // Find the polygon (if any) that encloses the mouse position. CadastralMapModel map = CadastralMapModel.Current; ISpatialIndex index = map.Index; IPointGeometry pg = PointGeometry.Create(pos); Polygon enc = new FindPointContainerQuery(index, pg).Result; // If it's different from what we previously had, remember the // new enclosing polygon. if (!Object.ReferenceEquals(enc, m_Polygon)) { // If we had something before, and we filled it, erase // the fill now. //DrawPolygon(false); // Remember the polygon we're now enclosed by (if any). m_Polygon = enc; // Draw the new polygon. //DrawPolygon(true); // Ensure any calculated position has been cleared m_AutoPosition = null; // See if a new orientation applies CheckOrientation(pg); // If the enclosing polygon does not have a label, use the // standard cursor and fill the polygon. Otherwise use the // gray cursor. SetCommandCursor(); } // Draw a rectangle representing the outline of the text. if (m_IsAutoPos && m_Polygon != null) { if (m_AutoPosition == null) { m_AutoPosition = m_Polygon.GetLabelPosition(Width, Height); } } if (m_IsAutoPos && m_AutoPosition != null) { DrawText(m_AutoPosition); } else { DrawText(pos); } }
internal override bool LButtonDown(IPosition p) { // Quit if we don't have a selected line if (m_Line == null) { AbortCommand(); return(false); } // Get the position on the selected line that is closest // to the supplied position. ISpatialDisplay draw = ActiveDisplay; ILength tol = new Length(0.001 * draw.MapScale); IPointGeometry pg = PointGeometry.Create(p); IPosition pos = m_Line.LineGeometry.GetClosest(pg, tol); if (pos == null) { MessageBox.Show("You appear to be moving the mouse too fast"); return(false); } // Obtain the position ratio uint pr = AttachPointOperation.GetPositionRatio(m_Line, pos); // Attach a new point to the line AttachPointOperation op = null; try { op = new AttachPointOperation(m_Line, pr); FeatureFactory ff = new FeatureFactory(op); ff.PointType = m_PointType; op.Execute(ff); // Ensure the draw includes the extra point (perhaps a bit of overkill to // draw just one point). Controller.RefreshAllDisplays(); } catch (Exception ex) { MessageBox.Show(ex.StackTrace, ex.Message); return(true); } // Exit the command if we're not supposed to repeat if (!m_Repeat) { AbortCommand(); } return(true); }
/// <summary> /// Constructor for a grazing intersection. If the supplied positions are /// actually closer than the coordinate resolution (1 micron), a simple /// intersection will be defined. /// </summary> /// <param name="p1">The 1st intersection.</param> /// <param name="p2">The 2nd intersection.</param> IntersectionData(IPointGeometry p1, IPointGeometry p2) { m_X1 = p1; if (!p1.IsCoincident(p2)) { m_X2 = p2; } m_SortValue = 0.0; m_Context1 = 0; m_Context2 = 0; }
/// <summary> /// Checks whether this window overlaps a position /// </summary> /// <param name="p">The position to examine</param> /// <returns>True if the position overlaps this window (may be exactly /// coincident with the perimeter)</returns> internal bool IsOverlap(IPointGeometry p) { ulong val = SpatialIndex.ToUnsigned(p.Easting.Microns); if (val < m_X.Min || val > m_X.Max) { return(false); } val = SpatialIndex.ToUnsigned(p.Northing.Microns); return(val >= m_Y.Min && val <= m_Y.Max); }
void WriteSegment(SegmentGeometry line) { Line acLine = new Line(); IPointGeometry start = line.Start; acLine.StartPoint = new Vector3d(start.X, start.Y, 0.0); IPointGeometry end = line.End; acLine.EndPoint = new Vector3d(end.X, end.Y, 0.0); acLine.Layer = m_Layer; m_Dxf.AddEntity(acLine); }
/// <summary> /// Constructs a new <c>MultiSegmentGeometry</c> /// </summary> /// <param name="start">The point at the start of the connection.</param> /// <param name="end">The point at the end of the connection.</param> /// <param name="positions">The ground positions to pack. The first & last positions must exactly /// match the position of the supplied end points.</param> internal MultiSegmentGeometry(ITerminal start, ITerminal end, IPointGeometry[] positions) : base(start, end) { if (positions==null || positions.Length==0) throw new ArgumentNullException(); if (positions.Length<=2) throw new ArgumentException("Not enough points for a multi-segment"); if (!start.IsCoincident(positions[0])) throw new ArgumentException("Start point doesn't coincide with first position"); if (!end.IsCoincident(positions[positions.Length-1])) throw new ArgumentException("End point doesn't coincide with last position"); m_Data = positions; m_Extent = LineStringGeometry.GetExtent(this); }
/// <summary> /// Creates a new <c>FindClosestQuery</c> (and executes it). The result of the query /// can then be obtained through the <c>Result</c> property. /// </summary> /// <param name="index">The spatial index to search</param> /// <param name="p">The search position.</param> /// <param name="radius">The search tolerance (expected to be greater than zero).</param> /// <param name="types">The type of objects to look for.</param> internal FindClosestQuery(ISpatialIndex index, IPosition p, ILength radius, SpatialType types) { if (types==0) throw new ArgumentNullException("Spatial type(s) not specified"); // If the user hasn't been specific, ensure we don't search for polygons! SpatialType useTypes = (types==SpatialType.All ? SpatialType.Feature : types); Debug.Assert((useTypes & SpatialType.Polygon)==0); // It's important to round off to the nearest micron. Otherwise you might not // get the desired results in situations where the search radius is zero. m_Position = PositionGeometry.Create(p); m_Radius = radius; m_Types = types; m_Result = null; m_Distance = m_Radius.Meters; // The query will actually involve a square window, not a circle. IWindow x = new Window(m_Position, radius.Meters * 2.0); index.QueryWindow(x, useTypes, OnQueryHit); }
/// <summary> /// Intersects 2 clockwise arcs that coincide with the perimeter of a circle. /// </summary> /// <param name="xsect">Intersection results.</param> /// <param name="circle">The circle the arcs coincide with</param> /// <param name="bc1">BC for 1st arc.</param> /// <param name="ec1">EC for 1st arc.</param> /// <param name="bc2">BC for 2nd arc.</param> /// <param name="ec2">EC for 2nd arc.</param> /// <returns>The number of intersections (0, 1, or 2). If non-zero, the intersections /// will be grazes.</returns> static uint ArcIntersect( IntersectionResult xsect , ICircleGeometry circle , IPointGeometry bc1 , IPointGeometry ec1 , IPointGeometry bc2 , IPointGeometry ec2) { // Define the start of the clockwise arc as a reference line, // and get the clockwise angle to it's EC. Turn reft = new Turn(circle.Center, bc1); double sector = reft.GetAngleInRadians(ec1); // Where do the BC and EC of the 2nd curve fall with respect // to the 1st curve's arc sector? double bcang = reft.GetAngleInRadians(bc2); double ecang = reft.GetAngleInRadians(ec2); if (bcang<sector) { if (ecang<bcang) { xsect.Append(bc2, ec1); xsect.Append(bc1, ec2); return 2; } if (ecang<sector) xsect.Append(bc2, ec2); else xsect.Append(bc2, ec1); return 1; } // The BC of the 2nd curve falls beyond the sector of the 1st ... // so we can't have any graze if the EC is even further on. if (ecang>bcang) return 0; // One graze ... if (ecang<sector) xsect.Append(bc1, ec2); else xsect.Append(bc1, ec1); return 1; }
/// <summary> /// Intersects a pair of clockwise arcs that sit on the same circle, and where ONE of /// the end points exactly matches. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="circle">The circle the arcs coincide with</param> /// <param name="bc1">The BC of the 1st arc</param> /// <param name="ec1">The EC of the 1st arc</param> /// <param name="bc2">The BC of the 2nd arc -- the matching end</param> /// <param name="ec2">The EC of the 2nd arc</param> /// <param name="isStartMatch">Specify <c>true</c> if <paramref name="bc2"/> matches an end /// point of the 1st arc. Specify <c>false</c> if <paramref name="ec2"/> matches an end /// point of the 1st arc.</param> /// <returns>The number of intersections (always 1).</returns> static uint ArcEndIntersect( IntersectionResult xsect , ICircleGeometry circle , IPointGeometry bc1 , IPointGeometry ec1 , IPointGeometry bc2 , IPointGeometry ec2 , bool isStartMatch) { bool bmatch = bc1.IsCoincident(bc2); bool ematch = ec1.IsCoincident(ec2); // If the two curves share the same BC or same EC if (bmatch || ematch) { // We've got some sort of graze ... // Check for total graze. if (bmatch && ematch) xsect.Append(bc1, ec1); else { // We've therefore got a partial graze. // If the length of this arc is longer than the other one, the graze is over the // length of the other one, and vice versa. Since the two curves share the same // radius and direction, the comparison just involves a comparison of the clockwise // angles subtended by the arcs. IPointGeometry centre = circle.Center; double ang1 = new Turn(centre, bc1).GetAngleInRadians(ec1); double ang2 = new Turn(centre, bc2).GetAngleInRadians(ec2); bool isThisGraze = (ang1 < ang2); if (isThisGraze) xsect.Append(bc1, ec1); else xsect.Append(bc2, ec2); } } else { // The only intersection is at the common end. if (bc1.IsCoincident(bc2) || ec1.IsCoincident(bc2)) xsect.Append(bc2); else xsect.Append(ec2); } return 1; }
internal static uint Intersect( IntersectionResult results , IPointGeometry a , IPointGeometry b , IPointGeometry p , IPointGeometry q) { // 04-APR-2003: This isn't supposed to happen, but if we've somehow // got a null segment, it NEVER intersects anything. if (a.IsCoincident(b)) return 0; // If the segment EXACTLY meets either end of the other segment, // it gets handled seperately. if (a.IsCoincident(p) || a.IsCoincident(q)) return EndIntersect(results, p, q, a, b); if (b.IsCoincident(p) || b.IsCoincident(q)) return EndIntersect(results, p, q, b, a); // Return if the windows don't overlap. Window winab = new Window(a, b); Window winpq = new Window(p, q); if (!winab.IsOverlap(winpq)) return 0; // Check whether this line is completely coincident with the other one. // double tolsq = Constants.XYTOLSQ; double tolsq = (Constants.XYRES*Constants.XYRES); uint xcase = 0; if (PointGeometry.IsCoincidentWith(a, p, q, tolsq)) xcase |= 0x08; if (PointGeometry.IsCoincidentWith(b, p, q, tolsq)) xcase |= 0x04; if (xcase==12) // bits 1100 { results.Append(a, b); return 1; } // Check the end points of the other line to this one. if (PointGeometry.IsCoincidentWith(p, a, b, tolsq)) xcase |= 0x02; if (PointGeometry.IsCoincidentWith(q, a, b, tolsq)) xcase |= 0x01; // Return intersections. Note that in cases 3,7,11, the intersections // are not necessarily ordered with respect to THIS segment. switch (xcase) { case 0: { // Try to get a simple intersection. Do not accept // virtual intersections, since they should have been // trapped via the calculation of the xcase. double xi, yi; int xcode = Geom.CalcIntersect(a.X, a.Y, b.X, b.Y, p.X, p.Y, q.X, q.Y, out xi, out yi, true); if (xcode < 0) { results.Append(xi, yi, 0.0); return 1; } return 0; } case 1: { results.Append(q); return 1; } case 2: { results.Append(p); return 1; } case 3: { results.Append(p, q); // order? return 1; } case 4: { results.Append(b); return 1; } case 5: { results.Append(q, b); return 1; } case 6: { results.Append(p, b); return 1; } case 7: { results.Append(p, q); // order? return 1; } case 8: { results.Append(a); return 1; } case 9: { results.Append(a, q); return 1; } case 10: { results.Append(a, p); return 1; } case 11: { results.Append(p, q); // order? return 1; } } // end switch throw new Exception("LineSegmentFeature.Intersect - Unexpected case"); }
/// <summary> /// Intersects a circle with a clockwise arc /// </summary> /// <param name="results"></param> /// <param name="c"></param> /// <param name="start"></param> /// <param name="end"></param> /// <param name="arcCircle"></param> /// <returns></returns> static uint Intersect(IntersectionResult xsect, ICircleGeometry c, IPointGeometry start, IPointGeometry end, ICircleGeometry arcCircle) { // If the circles are IDENTICAL, we've got a graze. if (CircleGeometry.IsCoincident(c, arcCircle, Constants.XYRES)) { xsect.Append(start, end); return 1; } // Intersect the 2 circles. IPosition x1, x2; uint nx = Intersect(c, arcCircle, out x1, out x2); // Return if no intersection. if (nx==0) return 0; // Remember the intersection(s) if they fall in the // curve's sector. Use a tolerance which is based on // the circle with the smaller radius (=> bigger angular // tolerance). IPointGeometry centre; double minrad; if (c.Radius < arcCircle.Radius) { minrad = c.Radius; centre = c.Center; } else { minrad = arcCircle.Radius; centre = arcCircle.Center; } // const FLOAT8 angtol = XYTOL/minrad; // const FLOAT8 angtol = 0.00002/minrad; // 20 microns double angtol = 0.002/minrad; // 2mm if (nx==1) { IPointGeometry loc = PointGeometry.Create(x1); if (Geom.IsInSector(loc, centre, start, end, angtol)) { xsect.Append(loc); return 1; } return 0; } else { // Two intersections. They are valid if they fall within the curve's sector. IPointGeometry loc1 = PointGeometry.Create(x1); IPointGeometry loc2 = PointGeometry.Create(x2); uint nok=0; if (Geom.IsInSector(loc1, centre, start, end, angtol)) { xsect.Append(loc1); nok++; } if (Geom.IsInSector(loc2, centre, start, end, angtol)) { xsect.Append(loc2); nok++; } return nok; } }
/// <summary> /// Sees whether the orientation of the new text should be altered. This /// occurs if the auto-angle capability is enabled, and the specified /// position is close to any visible line. /// </summary> /// <param name="refpos">The current mouse position (reference position /// for the new label)</param> /// <returns>True if the orientation angle got changed.</returns> bool CheckOrientation(IPointGeometry refpos) { // Return if the auto-angle function is disabled. if (!m_IsAutoAngle) return false; // Return if the auto-position function is enabled if (m_IsAutoPos) return false; // Try to get an orientation line LineFeature orient = GetOrientation(refpos); if (!Object.ReferenceEquals(orient, m_Orient)) ErasePainting(); m_Orient = null; if (orient == null) return false; // Locate the closest point on the line (we SHOULD find it, // but if we don't, just bail out) ISpatialDisplay display = ActiveDisplay; ILength tol = new Length(0.002 * display.MapScale); IPosition closest = orient.LineGeometry.GetClosest(refpos, tol); if (closest == null) return false; m_Orient = orient; if (m_Orient==null) return false; // Highlight the new orientation line m_Orient.Render(display, new HighlightStyle()); // Get the rotation angle IPointGeometry cg = PointGeometry.Create(closest); double rot = m_Orient.LineGeometry.GetRotation(cg); SetRotation(rot); return true; }
/// <summary> /// Returns a code indicating the relative position of a location with respect to /// a rectangular window (the Cohen & Sutherland clipping algorithm). /// </summary> /// <param name="p">The position of interest</param> /// <param name="sw">South-west corner of window</param> /// <param name="ne">North-east corner of window</param> /// <returns></returns> internal static byte GetPositionCode(IPointGeometry p, IPointGeometry sw, IPointGeometry ne) { byte code = 0; long e = p.Easting.Microns; if (e < sw.Easting.Microns) // p west of sw code |= 0x01; else if (e > ne.Easting.Microns) // p east of ne code |= 0x02; long n = p.Northing.Microns; if (n < sw.Northing.Microns) // p south of sw code |= 0x04; else if (n > ne.Northing.Microns) // p north of ne code |= 0x08; return code; }
/// <summary> /// Gets the point on this line that is closest to a specified position. /// </summary> /// <param name="p">The position to search from.</param> /// <param name="tol">Maximum distance from line to the search position</param> /// <returns>The closest position (null if the line is further away than the specified /// max distance)</returns> internal override IPosition GetClosest(IPointGeometry p, ILength tol) { return Make().GetClosest(p, tol); }
/// <summary> /// Gets the point on this line that is closest to a specified position. /// </summary> /// <param name="p">The position to search from.</param> /// <param name="tol">Maximum distance from line to the search position</param> /// <returns>The closest position (null if the line is further away than the specified /// max distance)</returns> internal override IPosition GetClosest(IPointGeometry p, ILength tol) { // Get the perpendicular point (or closest end) IPointGeometry s = Start; IPointGeometry e = End; double xp, yp; BasicGeom.GetPerpendicular(p.X, p.Y, s.X, s.Y, e.X, e.Y, out xp, out yp); // Ignore if point is too far away double t = tol.Meters; double dx = p.X - xp; if (dx > t) return null; double dy = p.Y - yp; if (dy > t) return null; double dsq = (dx*dx + dy*dy); if (dsq > t*t) return null; return new Position(xp, yp); }
/// <summary> /// Cuts back a horizontal line segment to the closest intersection with this line. /// Used in point in polygon. /// </summary> /// <param name="s">Start of horizontal segment.</param> /// <param name="e">End of segment (will be modified if segment intersects this line)</param> /// <param name="status">Return code indicating whether an error has arisen (returned /// as 0 if no error).</param> /// <returns>True if the horizontal line was cut back.</returns> internal override bool GetCloser(IPointGeometry s, ref PointGeometry e, out uint status) { return Make().GetCloser(s, ref e, out status); }
/// <summary> /// Calculates an angle that is parallel to this line (suitable for adding text) /// </summary> /// <param name="p">A significant point on the line (not used).</param> /// <returns>The rotation (in radians, clockwise from horizontal). Always greater /// than or equal to 0.0</returns> internal override double GetRotation(IPointGeometry p) { double xs = Start.X; double ys = Start.Y; double xe = End.X; double ye = End.Y; double dx = xe - xs; double dy = ye - ys; // Horizontal (to nearest mm) double ady = Math.Abs(dy); if (ady < 0.001) return 0.0; // Vertical (to nearest mm) double adx = Math.Abs(dx); if (adx < 0.001) return MathConstants.PIDIV2; // Get result in range (0,PIDIV2) double rotation = Math.Atan(ady/adx); // Stuff in the NE and SW quadrants needs to be tweaked. if ((dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0)) { rotation = -rotation; if (rotation < 0.0) rotation += MathConstants.PIMUL2; } return rotation; }
// Circle internal uint Intersect(IPointGeometry center, double radius) { ICircleGeometry circle = new CircleGeometry(center, radius); return m_IntersectedObject.LineGeometry.IntersectCircle(this, circle); }
/// <summary> /// Sees whether the orientation of the new text should be altered. This /// occurs if the auto-orient capability is enabled, and the specified /// position is close to any visible lne. /// </summary> /// <param name="posn">The position to use for making the check.</param> /// <returns></returns> LineFeature GetOrientation(IPointGeometry posn) { // The ground tolerance is 2mm at the draw scale. ISpatialDisplay display = ActiveDisplay; double tol = 0.002 * display.MapScale; // If we previously selected something, see if the search point // lies within tolerance. If so, there's no change. if (m_Orient != null) { double dist = m_Orient.Distance(posn).Meters; if (dist < tol) return m_Orient; } // Get the map to find the closest line CadastralMapModel map = CadastralMapModel.Current; ISpatialIndex index = map.Index; return (index.QueryClosest(posn, new Length(tol), SpatialType.Line) as LineFeature); }
/// <summary> /// Intersects this segment with another segment, where at least one end of the /// segment exactly coincides with one end of the other segment. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="start">The start of the other segment.</param> /// <param name="end">The end of the other segment.</param> /// <param name="xend">The end of THIS segment that coincides with one end /// of the other one (the geometry for either m_Start or m_End).</param> /// <param name="othend">The other end of THIS segment (may or may not coincide /// with one end of the other segment).</param> /// <returns>The number of intersections (always 1).</returns> static uint EndIntersect( IntersectionResult xsect , IPointGeometry start , IPointGeometry end , IPointGeometry xend , IPointGeometry othend) { // If the other end of this segment coincides with either end // of the other segment, we've got a total graze. if (othend.IsCoincident(start) || othend.IsCoincident(end)) { xsect.Append(start, end); return 1; } // Get the locations that define the longer segment, together // with the location that is different from the exactly matching end. IPointGeometry startLong; IPointGeometry endLong; IPointGeometry test; if (Geom.DistanceSquared(xend, othend) < Geom.DistanceSquared(start, end)) { test = othend; startLong = start; endLong = end; } else { startLong = xend; endLong = othend; if (xend.IsCoincident(start)) test = end; else test = start; } // If it is coincident (to within the resolution) AND the // position ratio of the perpendicular point is ON this // segment, it's a graze. double tolsq = (Constants.XYRES * Constants.XYRES); if (PointGeometry.IsCoincidentWith(test, startLong, endLong, tolsq)) { double prat = Geom.GetPositionRatio(test.X , test.Y , startLong.X , startLong.Y , endLong.X , endLong.Y); if (prat>0.0 && prat<1.0) { xsect.Append(xend, test); return 1; } } // ONE intersection at the end that exactly matches. xsect.Append(xend); return 1; }
/// <summary> /// Calculates an angle that is parallel to this line (suitable for adding text) /// </summary> /// <param name="p">A significant point on the line. In the case of lines /// that are multi-segments, the individual line segment that contains this /// position should be used to obtain the angle.</param> /// <returns>The rotation (in radians, clockwise from horizontal)</returns> internal override double GetRotation(IPointGeometry p) { return Make().GetRotation(p); }
/// <summary> /// Intersects a line segment with a clockwise arc, where at least one end of the /// segment exactly coincides with one end of the arc. /// </summary> /// <param name="xsect">The intersection results.</param> /// <param name="bc">The start of the clockwise arc.</param> /// <param name="ec">The end of the clockwise arc.</param> /// <param name="circle">The circle on which the arc lies.</param> /// <param name="xend">The end of the segment that coincides with one end of the arc.</param> /// <param name="othend">The other end of the segment (may or may not coincide /// with one end of the arc).</param> /// <returns>The number of intersections (either 1 or 2).</returns> static uint EndIntersect( IntersectionResult xsect , IPointGeometry bc , IPointGeometry ec , ICircleGeometry circle , IPointGeometry xend , IPointGeometry othend) { // If the other end is INSIDE the circle, the matching end // location is the one and only intersection. Allow a tolerance // that is consistent with the resolution of data (3 microns). IPointGeometry centre = circle.Center; double radius = circle.Radius; double minrad = (radius-Constants.XYTOL); double minrsq = (minrad*minrad); double othdsq = Geom.DistanceSquared(othend, centre); if (othdsq < minrsq) { xsect.Append(xend); return 1; } // If the other end of the segment also coincides with either // end of the curve, the segment is a chord of the circle that // the curve lies on. However, if it's REAL close, we need to // return it as a graze ... if (othend.IsCoincident(bc) || othend.IsCoincident(ec)) { // Get the midpoint of the chord. IPosition midseg = Position.CreateMidpoint(xend, othend); // If the distance from the centre of the circle to the // midpoint is REAL close to the curve, we've got a graze. if (Geom.DistanceSquared(midseg, centre) > minrsq) { xsect.Append(xend, othend); return 1; } // Two distinct intersections. xsect.Append(xend); xsect.Append(othend); return 2; } // If the other end is within tolerance of the circle, project // it to the circle and see if it's within the curve's sector. // If not, it's not really an intersect. double maxrad = (radius+Constants.XYTOL); double maxrsq = (maxrad*maxrad); if ( othdsq < maxrsq ) { // Check if the angle to the other end is prior to the EC. // If not, it's somewhere off the curve. Turn reft = new Turn(centre, bc); if (reft.GetAngleInRadians(othend) > reft.GetAngleInRadians(ec)) { xsect.Append(xend); return 1; } // And, like above, see if the segment grazes or not ... // Get the midpoint of the chord. IPosition midseg = Position.CreateMidpoint(xend, othend); // If the distance from the centre of the circle to the // midpoint is REAL close to the curve, we've got a graze. if (Geom.DistanceSquared(midseg, centre) > minrsq) { xsect.Append(xend, othend); return 1; } // Two distinct intersections. xsect.Append(xend); xsect.Append(othend); return 2; } // That leaves us with the other end lying somewhere clearly // outside the circle. However, we could still have a graze. // Make sure the BC/EC are EXACTLY coincident with the circle (they // may have been rounded off if the curve has been intersected // with a location that's within tolerance of the circle). IPosition trueBC, trueEC; Position.GetCirclePosition(bc, centre, radius, out trueBC); Position.GetCirclePosition(ec, centre, radius, out trueEC); // As well as the end of the segment that meets the curve. IPosition trueXend = (xend.IsCoincident(bc) ? trueBC : trueEC); // Intersect the segment with the complete circle (this does // NOT return an intersection at the other end, even if it is // really close ... we took care of that above). // The intersection must lie ON the segment (not sure about this though). IPosition othvtx = othend; IPosition x1, x2; bool isTangent; uint nx = Geom.IntersectCircle(centre, radius, trueXend, othvtx, out x1, out x2, out isTangent, true); // If we got NOTHING, that's unexpected, since one end exactly coincides // with the circle. In that case, just return the matching end (NOT // projected to the true position). if (nx==0) { xsect.Append(xend); return 1; } // If we got 2 intersections, pick the one that's further away than the matching end. if (nx==2 && Geom.DistanceSquared(x2, trueXend) > Geom.DistanceSquared(x1, trueXend)) x1 = x2; // That leaves us with ONE intersection with the circle ... now // confirm that it actually intersects the arc! Turn refbc = new Turn(centre, trueBC); double eangle = refbc.GetAngleInRadians(trueEC); double xangle = refbc.GetAngleInRadians(x1); if (xangle > eangle) { xsect.Append(xend); return 1; } // Get the midpoint of the segment that connects the intersection to the true end. IPosition midx = Position.CreateMidpoint(trueXend, x1); // If the midpoint does NOT graze the circle, we've got 2 distinct intersections. // 25-NOV-99: Be realistic about it (avoid meaningless sliver polygons that are // less than 0.1mm wide on the ground). // if ( midx.DistanceSquared(centre) < minrsq ) { double rdiff = Geom.Distance(midx, centre) - radius; if (Math.Abs(rdiff) > 0.0001) { xsect.Append(xend); xsect.Append(x1); return 2; } // We've got a graze, but possibly one that can be ignored(!). To // understand the reasoning here, bear in mind that lines get cut // only so that network topology can be formed. To do that, 2 // orientation points are obtained for the lines incident on xend. // For the segment, the orientation point is the other end of the // segment. For the curve, it's a position 5 metres along the // curve (or the total curve length if it's not that long). So // if the graze is closer than the point that will be used to // get the orientation point, we can ignore the graze, since it // does not provide any useful info. // Given that it's a graze, assume that it's ok to work out // the arc distance as if it was straight. double dsqx = Geom.DistanceSquared(trueXend, x1); // If it's closer than 4m (allow some leeway, seeing how we've // just done an approximation), ignore the intersection. If it's // actually between 4 and 5 metres, it shouldn't do any harm // to make a split there (although it's kind of redundant). if (dsqx < 16.0) { xsect.Append(xend); return 1; } // It's a graze. //CeVertex vxend(xend); // wants a vertex xsect.Append(xend, x1); return 1; }
/// <summary> /// Intersects a line segment with the data describing a clockwise curve. /// </summary> /// <param name="result">The intersection results.</param> /// <param name="a">The start of the line segment</param> /// <param name="b">The end of the line segment</param> /// <param name="start">The start of the clockwise arc.</param> /// <param name="end">The end of the clockwise arc.</param> /// <param name="circle">The circle on which the arc lies.</param> /// <returns></returns> static uint Intersect( IntersectionResult result , IPointGeometry a , IPointGeometry b , IPointGeometry start , IPointGeometry end , ICircleGeometry circle) { // If the segment exactly meets either end of the curve, it gets handled seperately. if (a.IsCoincident(start) || a.IsCoincident(end)) return EndIntersect(result, start, end, circle, a, b); if (b.IsCoincident(start) || b.IsCoincident(end)) return EndIntersect(result, start, end, circle, b, a); // Get circle definition. IPointGeometry centre = circle.Center; double radius = circle.Radius; // Get up to 2 intersections with the circle. IPosition x1, x2; bool isGraze; uint nx = GetXCircle(a, b, centre, radius, out x1, out x2, out isGraze); // Return if no intersections with the circle. if (nx==0) return 0; // If an intersection is really close to the end of an arc, force it to be there. if (Geom.DistanceSquared(start, x1) < Constants.XYTOLSQ) x1 = start; else if (Geom.DistanceSquared(end, x1) < Constants.XYTOLSQ) x1 = end; if (nx==2) { if (Geom.DistanceSquared(start, x2) < Constants.XYTOLSQ) x2 = start; else if (Geom.DistanceSquared(end, x2) < Constants.XYTOLSQ) x2 = end; } // Determine whether the intersections fall within the sector // that's defined by the clockwise curve (grazes need a bit // more handling, so do that after we see how to do the non- // grazing case. if ( !isGraze ) { PointGeometry xloc1 = PointGeometry.Create(x1); double lintol = Constants.XYTOL / radius; // If we only got one intersect, and it's out of sector, that's us done. if (nx==1) { if (!BasicGeom.IsInSector(xloc1, centre, start, end, lintol)) return 0; result.Append(x1); return 1; } // Two intersections with the circle ... if (BasicGeom.IsInSector(xloc1, centre, start, end, lintol)) result.Append(x1); else nx--; PointGeometry xloc2 = PointGeometry.Create(x2); if (BasicGeom.IsInSector(xloc2, centre, start, end, lintol)) result.Append(x2); else nx--; return nx; } // That leaves us with the case where this segment grazes the // circle. GetXCircle is always supposed to return nx==2 in that // case (they're also supposed to be arranged in the same // direction as this segment). Debug.Assert(nx==2); // Get the clockwise angle subtended by the circular arc. Add // on a tiny bit to cover numerical comparison problems. Turn reft = new Turn(centre, start); double maxangle = reft.GetAngleInRadians(end) + Constants.TINY; // Get the clockwise angles to both intersections. double a1 = reft.GetAngleInRadians(x1); double a2 = reft.GetAngleInRadians(x2); // No graze if both are beyond the arc sector. if (a1>maxangle && a2>maxangle) return 0; // If both intersects are within sector, the only thing to watch // out for is a case where the graze apparently occupies more // than half a circle. In that case, we've actually got 2 grazes. if (a1<maxangle && a2<maxangle) { if (a2>a1 && (a2-a1)>Constants.PI) { result.Append(x1, start); result.Append(end, x2); return 2; } if (a1>a2 && (a1-a2)>Constants.PI) { result.Append(start, x2); result.Append(x1, end); return 2; } // So it's just a regular graze. result.Append(x1, x2); return 1; } // That's covered the cases where both intersects are either // both in sector, or both out. Return the intersection that's // in sector as a simple intersection (NOT a graze). // This is a cop-out. We should really try to figure out which // portion of the curve is grazing, but the logic for doing so // is surprisingly complex, being subject to a variety of special // cases. By returning the intersect as a simple intersect, I'm // hoping it covers most cases. if (a1 < maxangle) result.Append(x1); else result.Append(x2); return 1; }
/// <summary> /// Attempts to find a location that can act as a terminal for a polygon boundary. /// This either refers to a user-perceived point feature, or an intersection /// point (as added via a prior call to <see cref="AddIntersection"/>). /// </summary> /// <param name="p">The position of interest</param> /// <remarks>The corresponding terminal (null if nothing found). This should either /// be an instance of <see cref="PointFeature"/> or <see cref="Intersection"/>.</remarks> internal ITerminal FindTerminal(IPointGeometry p) { // Search the base index for a real point feature PointFeature pf = (base.QueryClosest(p, Length.Zero, SpatialType.Point) as PointFeature); if (pf!=null) return pf; // Search for an intersection return (m_ExtraData.QueryClosest(p, Length.Zero, SpatialType.Point) as Intersection); }
/// <summary> /// Intersects a line segment with a circle /// </summary> /// <param name="result"></param> /// <param name="a">The start of the line segment</param> /// <param name="b">The end of the line segment</param> /// <param name="c">The center of the circle</param> /// <param name="r">The radius of the circle</param> /// <returns></returns> static uint Intersect(IntersectionResult result, IPointGeometry a, IPointGeometry b, IPointGeometry c, double r) { // Get intersections (if any). Return if nothing. IPosition x1, x2; bool isGraze; uint nx = GetXCircle(a, b, c, r, out x1, out x2, out isGraze); if (nx==0) return 0; // If we got just one intersection, it's a simple intersect (even // if it's a tangent to the circle). if (nx==1) { result.Append(x1); return 1; } // That leaves us with two intersections, which may or may // not be grazing the circle. if (isGraze) { result.Append(x1, x2); return 1; } result.Append(x1); result.Append(x2); return 2; }
/// <summary> /// Cuts back a horizontal line segment to the closest intersection with this line. /// Used in point in polygon. /// </summary> /// <param name="s">Start of horizontal segment.</param> /// <param name="e">End of segment (will be modified if segment intersects this line)</param> /// <param name="status">Return code indicating whether an error has arisen (returned /// as 0 if no error).</param> /// <returns>True if the horizontal line was cut back.</returns> internal override bool GetCloser(IPointGeometry s, ref PointGeometry e, out uint status) { status = 0; // Remember the initial end of segment PointGeometry initEnd = new PointGeometry(e); // Represent the horizontal segment in a class of its own HorizontalRay hseg = new HorizontalRay(s, e.X-s.X); if (!hseg.IsValid) { status = 1; return false; } // Get relative position code for the start of the line. If it's // somewhere on the horizontal segment, cut the line back. byte scode = Geom.GetPositionCode(Start, s, e); if (scode==0) e = new PointGeometry(Start); // Get the position code for the end of the line segment byte ecode = Geom.GetPositionCode(End, s, e); // If it's coincident with the horizontal segment, cut the // line back. Otherwise see whether there is any potential // intersection to cut back to. if (ecode==0) e = new PointGeometry(End); else if ((scode & ecode)==0) { IPosition x = null; if (hseg.Intersect(Start, End, ref x)) e = new PointGeometry(x); } // Return flag to indicate whether we got closer or not. return (!e.IsCoincident(initEnd)); }
// Segment internal uint Intersect(IPointGeometry start, IPointGeometry end) { ILineSegmentGeometry seg = new LineSegmentGeometry(start, end); return m_IntersectedObject.LineGeometry.IntersectSegment(this, seg); }