/// <summary> /// Initializes a new instance of the <see cref="SplitEdgeResult"/> structure with the /// specified <see cref="SubdivisionEdge"/> instances.</summary> /// <param name="originEdge"> /// The <see cref="SubdivisionEdge"/> with the same <see cref="SubdivisionEdge.Origin"/> as /// the instance that has been split.</param> /// <param name="destinationEdge"> /// The <see cref="SubdivisionEdge"/> with the same <see /// cref="SubdivisionEdge.Destination"/> as the instance that has been split.</param> /// <param name="createdEdge"> /// The <see cref="SubdivisionEdge"/> that has been newly created for one of the two parts /// resulting from the split, if any; otherwise, a null reference.</param> /// <param name="isEdgeDeleted"> /// <c>true</c> if the <see cref="SubdivisionEdge"/> to be split has been deleted because /// both parts were duplicated by existing instances; otherwise, <c>false</c>.</param> public SplitEdgeResult( SubdivisionEdge originEdge, SubdivisionEdge destinationEdge, SubdivisionEdge createdEdge, bool isEdgeDeleted) { OriginEdge = originEdge; DestinationEdge = destinationEdge; CreatedEdge = createdEdge; IsEdgeDeleted = isEdgeDeleted; }
/// <summary> /// Initializes a new instance of the <see cref="SubdivisionFace"/> class with the specified /// containing <see cref="Subdivision"/>, unique key, and outer and inner boundaries. /// </summary> /// <param name="owner"> /// The <see cref="Subdivision"/> that contains the <see cref="SubdivisionFace"/>.</param> /// <param name="key"> /// The unique key of the <see cref="SubdivisionFace"/> within its <paramref name="owner"/>. /// </param> /// <param name="outerEdge"> /// A <see cref="SubdivisionEdge"/> on the outer boundary of the <see /// cref="SubdivisionFace"/>. This argument may be a null reference.</param> /// <param name="innerEdges"> /// An <see cref="IEnumerable{T}"/> collection containing one <see cref="SubdivisionEdge"/> /// on each inner boundary of the <see cref="SubdivisionFace"/>. This argument may be a null /// reference.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="owner"/> is a null reference.</exception> /// <remarks> /// This constructor is intended for unit testing.</remarks> internal SubdivisionFace(Subdivision owner, int key, SubdivisionEdge outerEdge, IEnumerable <SubdivisionEdge> innerEdges) : this(owner, key) { _outerEdge = outerEdge; if (innerEdges != null) { _innerEdges = new ListEx <SubdivisionEdge>(innerEdges); } }
/// <summary> /// Adds the specified <see cref="SubdivisionEdge"/> to the <see cref="InnerEdges"/> /// collection.</summary> /// <param name="edge"> /// The <see cref="SubdivisionEdge"/> to add.</param> /// <remarks> /// <b>AddInnerEdge</b> first creates a new collection that backs the <see /// cref="InnerEdges"/> property, if necessary.</remarks> internal void AddInnerEdge(SubdivisionEdge edge) { Debug.Assert(edge != null); if (_innerEdges == null) { _innerEdges = new ListEx <SubdivisionEdge>(); } _innerEdges.Add(edge); }
/// <summary> /// Updates the <see cref="SubdivisionFace"/> keys in the specified dictionaries after the /// specified <see cref="SubdivisionEdge"/> has been split.</summary> /// <param name="edge"> /// The <see cref="SubdivisionEdge"/> whose splitting resulted in the current <see /// cref="SplitEdgeResult"/>.</param> /// <param name="edgeToFace1"> /// An <see cref="Int32Dictionary{T}"/> that maps the keys of any existing half-edges to the /// keys of the incident bounded <see cref="Subdivision.Faces"/> of the corresponding <see /// cref="Subdivision.Edges"/> in a first <see cref="Subdivision"/>.</param> /// <param name="edgeToFace2"> /// An <see cref="Int32Dictionary{T}"/> that maps the keys of any existing half-edges to the /// keys of the incident bounded <see cref="Subdivision.Faces"/> of the corresponding <see /// cref="Subdivision.Edges"/> in a second <see cref="Subdivision"/>.</param> /// <remarks> /// <b>UpdateFaces</b> ensures that the mapping between original and intersected faces /// established by the <see cref="Subdivision.Intersection"/> algorithm is kept up-to-date /// when edge splitting results in a valid <see cref="CreatedEdge"/>.</remarks> public void UpdateFaces(SubdivisionEdge edge, Int32Dictionary <Int32> edgeToFace1, Int32Dictionary <Int32> edgeToFace2) { if (CreatedEdge == null) { return; } int face; if (edgeToFace1.TryGetValue(edge._key, out face)) { Debug.Assert(face != 0); edgeToFace1.Add(CreatedEdge._key, face); } if (edgeToFace2.TryGetValue(edge._key, out face)) { Debug.Assert(face != 0); edgeToFace2.Add(CreatedEdge._key, face); } }
/// <summary> /// Moves the incident half-edge on one of the boundaries of the <see /// cref="SubdivisionFace"/> from the specified <see cref="SubdivisionEdge"/> to another /// specified instance.</summary> /// <param name="oldEdge"> /// The incident <see cref="SubdivisionEdge"/> to replace with <paramref name="newEdge"/>. /// </param> /// <param name="newEdge"> /// The incident <see cref="SubdivisionEdge"/> that replaces <paramref name="oldEdge"/>. /// </param> /// <returns> /// A <see cref="MoveEdgeResult"/> value indicating which <see cref="SubdivisionFace"/> /// properties were changed, if any.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="oldEdge"/> or <paramref name="newEdge"/> is a null reference. /// </exception> /// <remarks><para> /// If <see cref="OuterEdge"/> equals the specified <paramref name="oldEdge"/>, /// <b>MoveEdge</b> sets that property to the specified <paramref name="newEdge"/>. /// Otherwise, <b>MoveEdge</b> sets the first <see cref="InnerEdges"/> element that equals /// <paramref name="oldEdge"/> to <paramref name="newEdge"/>, if any. /// </para><para> /// Unlike the other <see cref="MoveEdge(SubdivisionEdge)"/> overload, this overload does /// not check the <see cref="SubdivisionEdge.Twin"/> of <paramref name="oldEdge"/>, nor /// remove single-edge cycles.</para></remarks> internal MoveEdgeResult MoveEdge(SubdivisionEdge oldEdge, SubdivisionEdge newEdge) { if (_outerEdge == oldEdge) { _outerEdge = newEdge; return(MoveEdgeResult.OuterChanged); } if (_innerEdges != null) { for (int i = 0; i < _innerEdges.Count; i++) { if (_innerEdges[i] == oldEdge) { _innerEdges[i] = newEdge; return(MoveEdgeResult.InnerChanged); } } } return(MoveEdgeResult.Unchanged); }
/// <overloads> /// Moves the incident half-edge on one of the boundaries of the <see /// cref="SubdivisionFace"/>.</overloads> /// <summary> /// Moves the incident half-edge on one of the boundaries of the <see /// cref="SubdivisionFace"/> away from the specified <see cref="SubdivisionEdge"/>. /// </summary> /// <param name="oldEdge"> /// The incident <see cref="SubdivisionEdge"/> to replace with another half-edge on the same /// boundary of the <see cref="SubdivisionFace"/>.</param> /// <returns> /// A <see cref="MoveEdgeResult"/> value indicating which <see cref="SubdivisionFace"/> /// properties were changed, if any.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="oldEdge"/> is a null reference.</exception> /// <remarks><para> /// If <see cref="OuterEdge"/> equals the specified <paramref name="oldEdge"/> or its twin, /// <b>MoveEdge</b> sets <see cref="OuterEdge"/> to the result of <see /// cref="SubdivisionEdge.GetOtherCycleEdge"/> for the specified <paramref name="oldEdge"/>. /// </para><para> /// Otherwise, <b>MoveEdge</b> searches for an <see cref="InnerEdges"/> element that equals /// <paramref name="oldEdge"/> or its twin. On success, <b>MoveEdge</b> removes that element /// if the cycle contains no other half-edges; otherwise, <b>MoveEdge</b> sets that element /// to the result of <see cref="SubdivisionEdge.GetOtherCycleEdge"/> for <paramref /// name="oldEdge"/>.</para></remarks> internal MoveEdgeResult MoveEdge(SubdivisionEdge oldEdge) { SubdivisionEdge oldTwin = oldEdge._twin; if (_outerEdge == oldEdge || _outerEdge == oldTwin) { _outerEdge = _outerEdge.GetOtherCycleEdge(oldEdge); Debug.Assert(_outerEdge != null); return(MoveEdgeResult.OuterChanged); } if (_innerEdges != null) { for (int i = 0; i < _innerEdges.Count; i++) { SubdivisionEdge innerEdge = _innerEdges[i]; if (innerEdge == oldEdge || innerEdge == oldTwin) { if (oldEdge._next == oldTwin && oldEdge._previous == oldTwin) { _innerEdges.RemoveAt(i); if (_innerEdges.Count == 0) { _innerEdges = null; } return(MoveEdgeResult.InnerRemoved); } _innerEdges[i] = innerEdge.GetOtherCycleEdge(oldEdge); Debug.Assert(_innerEdges[i] != null); return(MoveEdgeResult.InnerChanged); } } } return(MoveEdgeResult.Unchanged); }
/// <summary> /// Initializes a new instance of the <see cref="SubdivisionElement"/> structure with the /// specified <see cref="SubdivisionEdge"/>.</summary> /// <param name="edge"> /// The <see cref="SubdivisionEdge"/> stored in the <see cref="SubdivisionElement"/>. /// </param> /// <remarks> /// <see cref="ElementType"/> is set to <see cref="SubdivisionElementType.Edge"/>.</remarks> public SubdivisionElement(SubdivisionEdge edge) { ElementType = SubdivisionElementType.Edge; _value = edge; _vertex = PointD.Empty; }
/// <summary> /// Finds the half-edge bounding the <see cref="SubdivisionFace"/> that is nearest to and /// facing the specified coordinates.</summary> /// <param name="q"> /// The <see cref="PointD"/> coordinates to locate.</param> /// <param name="distance"> /// Returns the distance between <paramref name="q"/> and the returned <see /// cref="SubdivisionEdge"/>, if any; otherwise, <see cref="Double.MaxValue"/>.</param> /// <returns><para> /// The <see cref="SubdivisionEdge"/> on any outer or inner boundaries of the <see /// cref="SubdivisionFace"/> with the smallest distance to and facing <paramref name="q"/>. /// </para><para>-or-</para><para> /// A null reference if the <see cref="SubdivisionFace"/> is completely unbounded. /// </para></returns> /// <remarks><para> /// <b>FindNearestEdge</b> traverses the <see cref="OuterEdge"/> boundary and all <see /// cref="InnerEdges"/> boundaries, computing the distance from <paramref name="q"/> to each /// <see cref="SubdivisionEdge"/>. This is an O(n) operation where n is the number of /// half-edges incident on the <see cref="SubdivisionFace"/>. /// </para><para> /// If <paramref name="q"/> is nearest to an edge that belongs to a zero-area protrusion /// into the <see cref="SubdivisionFace"/>, <b>FindNearestEdge</b> returns the twin /// half-edge that faces <paramref name="q"/>, according to its <see /// cref="SubdivisionEdge.Face"/> orientation.</para></remarks> public SubdivisionEdge FindNearestEdge(PointD q, out double distance) { distance = Double.MaxValue; SubdivisionEdge nearestEdge = null; // find smallest distance to any outer cycle edge if (_outerEdge != null) { SubdivisionEdge edge = _outerEdge; do { double d = edge.ToLine().DistanceSquared(q); if (distance > d) { distance = d; if (d == 0) { return(edge); } nearestEdge = edge; } edge = edge._next; } while (edge != _outerEdge); } // find smallest distance to any inner cycle edge if (_innerEdges != null) { for (int i = 0; i < _innerEdges.Count; i++) { SubdivisionEdge innerEdge = _innerEdges[i]; SubdivisionEdge edge = innerEdge; do { double d = edge.ToLine().DistanceSquared(q); if (distance > d) { distance = d; if (d == 0) { return(edge); } nearestEdge = edge; } edge = edge._next; } while (edge != innerEdge); } } if (nearestEdge == null) { return(null); } // check twin in case of zero-area protrusion if (nearestEdge._twin._face == this) { LineLocation location = nearestEdge.ToLine().Locate(q); if (location == LineLocation.Right) { nearestEdge = nearestEdge._twin; } } distance = Math.Sqrt(distance); return(nearestEdge); }