public override VectorPoint Copy() { MoveToPoint point = new MoveToPoint(X, Y); point.NormalX = NormalX; point.NormalY = NormalY; return(point); }
/// <summary>Moves the current pen location to the given point. Used when drawing paths.</summary> public void MoveTo(float x, float y) { // We need to add the first end: MoveToPoint point = new MoveToPoint(x, y); AddPathNode(point); CloseNode = point; }
/// <summary>Adds the given node to the start of the path. Must be a moveTo unless it's a temp thing. /// See AddPathNode to add to the end.</summary> public void AddPathNodeStart(VectorPoint point) { PathNodeCount++; if (FirstPathNode == null) { if (point.Unloaded) { FirstPathNode = LatestPathNode = point; } else { MoveToPoint move = point as MoveToPoint; if (move == null) { // Add a blank MoveTo - this means that moveTo's are always the close nodes. move = new MoveToPoint(0f, 0f); FirstPathNode = LatestPathNode = move; CloseNode = move; PathNodeCount++; point.Previous = move; LatestPathNode = move.Next = point; } else { FirstPathNode = LatestPathNode = point; CloseNode = move; } } } else { // Hook it onto the start: point.Previous = null; point.Next = FirstPathNode; FirstPathNode.Previous = point; FirstPathNode = point; } }
/// <summary>Adds the given path onto the end of this one.</summary> public void Append(VectorPath path) { if (LatestPathNode == null) { // This path just becomes the same as the given one: FirstPathNode = path.FirstPathNode; LatestPathNode = path.LatestPathNode; PathNodeCount = path.PathNodeCount; CloseNode = path.CloseNode; } else { // Add the first node: AddPathNode(path.FirstPathNode); LatestPathNode = path.LatestPathNode; // -1 because Add already added the first node. PathNodeCount += path.PathNodeCount - 1; CloseNode = path.CloseNode; } }
public override VectorPoint Copy() { MoveToPoint point = new MoveToPoint(X, Y); return(point); }
/// <summary>Sorts this path such that any holes it contains begin closest to it's containing contour. /// This essentially allows paths with holes (think hole in o!) to be correctly triangulated.</summary> public void HoleSort(){ // Already sorted? if(HoleSorted){ return; } HoleSorted=true; if(FirstPathNode==null){ return; } // Have we actually got any holes? // Look for a close node followed by a node contained within the previously closed shape. // All the first nodes of each contour: List<VectorPoint> firstNodes=new List<VectorPoint>(1); // Add the first one: firstNodes.Add(FirstPathNode); // Find all the contours: VectorPoint current=FirstPathNode.Next; while(current!=null){ if(current.IsClose){ if(current.Next!=null){ // The following node is a new contour: firstNodes.Add(current.Next); } } current=current.Next; } // How many have we got? int contourCount=firstNodes.Count; // Next, for each contour, check if it is contained in any of the previous contours: for(int i=1;i<contourCount;i++){ // Grab the node: VectorPoint node=firstNodes[i]; // Get it's coords: float currentX=node.X; float currentY=node.Y; // Do any previous contours contain the first node? for(int c=i-1;c>=0;c--){ // Get the shapes end node - thats the previous node of the following contour: VectorPoint shapeEnd=firstNodes[c+1].Previous; // Get the shapes start node: VectorPoint shapeStart=firstNodes[c]; // Does this contour contain the current point? if(Contains(currentX,currentY,shapeStart,shapeEnd)){ // Yep it does! We have a hole (or a fill inside a hole). // We're now going to shuffle it. // We need it to connect to the nearest node of it's containing contour. VectorPoint nearest=Nearest(currentX,currentY,shapeStart,shapeEnd); if(nearest!=shapeEnd){ // Here comes the "sort" - it's a simple process; // Think of it as simply moving a virtual line which goes between the inner shape and the outer shape. // Get the last vert of this hole shape: VectorPoint lastOfShape; if(i==contourCount-1){ lastOfShape=LatestPathNode; }else{ lastOfShape=firstNodes[i+1].Previous; } // Clear close status - this will cause it to act as if there's two nodes on top of each other: lastOfShape.IsClose=false; // Get the one that comes after nearest: VectorPoint nearestNext=nearest.Next; // Get the following node: VectorPoint originalNext=lastOfShape.Next; // We're about to: // - Remove the shape from the set // - Add a new node to the end of the shape located at nearest. // - Insert the result between nearest and it's follower. // Pop the shape out: shapeEnd.Next=originalNext; if(originalNext==null){ LatestPathNode=shapeEnd; }else{ originalNext.Previous=shapeEnd; } // Duplicate nearest with a moveto node: MoveToPoint point=new MoveToPoint(nearest.X,nearest.Y); point.Previous=lastOfShape; point.Next=nearestNext; PathNodeCount++; if(nearestNext==null){ LatestPathNode=point; }else{ nearestNext.Previous=point; } // Add shape between nearest and it's duplicate. nearest.Next=node; node.Previous=nearest; lastOfShape.Next=point; } // This return isn't quite correct, but it's a safety measure. // It's because we may have further holes, but they will test if their inside this one. return; //break; } } } }
/// <summary>Sorts this path such that any holes it contains begin closest to it's containing contour. /// This essentially allows paths with holes (think hole in o!) to be correctly triangulated.</summary> public void HoleSort() { // Already sorted? if (HoleSorted) { return; } HoleSorted = true; if (FirstPathNode == null) { return; } // Have we actually got any holes? // Look for a close node followed by a node contained within the previously closed shape. // All the first nodes of each contour: List <PathSegment> contours = GetContours(); // How many have we got? int contourCount = contours.Count; // Next, for each contour, check if it is contained in any of the previous contours: for (int i = 0; i < contourCount; i++) { // Grab the contour: PathSegment contour = contours[i]; // Get it's coords: float currentX = contour.First.X; float currentY = contour.First.Y; // Do any of the other contours contain the first node of this one? for (int c = 0; c < contourCount; c++) { // Get the potential container contour: PathSegment container = contours[c]; if (container == null || container == contour) { continue; } // Does this contour contain the current point? if (container.Contains(currentX, currentY)) { // Yep it does! We have a hole. // No longer a valid contour: contours[i] = null; // We're now going to shuffle it. // We need it to connect to the nearest node of it's containing contour. // Think of it as simply moving a virtual line which goes between the inner shape and the outer shape. // Get the nearest node: VectorPoint nearest = container.Nearest(currentX, currentY); while (!nearest.HasLine) { nearest = nearest.Next; } // Last is no longer a close. // This will cause it to act as if there's two nodes on top of each other: contour.Last.IsClose = false; // We're about to: // - Remove the shape from the set // - Add a new node to the end of the shape located at nearest. // - Insert the result between nearest and it's follower. // Pop the shape out: contour.Remove(); // Get current one after nearest: VectorPoint nearestNext = nearest.Next; // Add it back in at nearest. // Duplicate nearest first - it'll be the "receiving" side: MoveToPoint point = new MoveToPoint(nearest.X, nearest.Y); point.Previous = contour.Last; point.Next = nearestNext; PathNodeCount++; if (nearestNext == null) { LatestPathNode = point; } else { nearestNext.Previous = point; } nearest.Next = contour.First; contour.First.Previous = nearest; contour.Last.Next = point; // All done with this contour: break; } } } }
/// <summary>Remove a point and may insert a MoveTo /// to ensure the visual appearance of the rest of the path is unaffected.</summary> public void RemoveVisually(VectorPoint point) { // First, do the actual removal (inline version of Remove): if (point.Previous == null) { FirstPathNode = point.Next; } else { point.Previous.Next = point.Next; } if (point.Next == null) { LatestPathNode = point.Previous; } else { point.Next.Previous = point.Previous; } // If it's a MoveTo, do nothing else: if (point is MoveToPoint) { return; } // If the previous node is a MoveTo, move it to points location: VectorPoint previous = point.Previous; if (previous != null && (previous is MoveToPoint)) { // Move that MoveTo to points location: previous.X = point.X; previous.Y = point.Y; } else { // Otherwise, create a MoveTo and insert where our point was. MoveToPoint moveTo = new MoveToPoint(point.X, point.Y); if (point.Previous == null) { FirstPathNode = moveTo; } else { moveTo.Previous = point.Previous; point.Previous.Next = moveTo; } if (point.Next == null) { LatestPathNode = moveTo; } else { moveTo.Next = point.Next; point.Next.Previous = moveTo; } } }
//--------------------------------------
/// <summary>Sorts this path such that any holes it contains begin closest to it's containing contour. /// This essentially allows paths with holes (think hole in o!) to be correctly triangulated.</summary> public void HoleSort() { // Already sorted? if (HoleSorted) { return; } HoleSorted = true; if (FirstPathNode == null) { return; } // Have we actually got any holes? // Look for a close node followed by a node contained within the previously closed shape. // All the first nodes of each contour: List <VectorPoint> firstNodes = new List <VectorPoint>(1); // Add the first one: firstNodes.Add(FirstPathNode); // Find all the contours: VectorPoint current = FirstPathNode.Next; while (current != null) { if (current.IsClose) { if (current.Next != null) { // The following node is a new contour: firstNodes.Add(current.Next); } } current = current.Next; } // How many have we got? int contourCount = firstNodes.Count; // Next, for each contour, check if it is contained in any of the previous contours: for (int i = 1; i < contourCount; i++) { // Grab the node: VectorPoint node = firstNodes[i]; // Get it's coords: float currentX = node.X; float currentY = node.Y; // Do any previous contours contain the first node? for (int c = i - 1; c >= 0; c--) { // Get the shapes end node - thats the previous node of the following contour: VectorPoint shapeEnd = firstNodes[c + 1].Previous; // Get the shapes start node: VectorPoint shapeStart = firstNodes[c]; // Does this contour contain the current point? if (Contains(currentX, currentY, shapeStart, shapeEnd)) { // Yep it does! We have a hole (or a fill inside a hole). // We're now going to shuffle it. // We need it to connect to the nearest node of it's containing contour. VectorPoint nearest = Nearest(currentX, currentY, shapeStart, shapeEnd); if (nearest != shapeEnd) { // Here comes the "sort" - it's a simple process; // Think of it as simply moving a virtual line which goes between the inner shape and the outer shape. // Get the last vert of this hole shape: VectorPoint lastOfShape; if (i == contourCount - 1) { lastOfShape = LatestPathNode; } else { lastOfShape = firstNodes[i + 1].Previous; } // Clear close status - this will cause it to act as if there's two nodes on top of each other: lastOfShape.IsClose = false; // Get the one that comes after nearest: VectorPoint nearestNext = nearest.Next; // Get the following node: VectorPoint originalNext = lastOfShape.Next; // We're about to: // - Remove the shape from the set // - Add a new node to the end of the shape located at nearest. // - Insert the result between nearest and it's follower. // Pop the shape out: shapeEnd.Next = originalNext; if (originalNext == null) { LatestPathNode = shapeEnd; } else { originalNext.Previous = shapeEnd; } // Duplicate nearest with a moveto node: MoveToPoint point = new MoveToPoint(nearest.X, nearest.Y); point.Previous = lastOfShape; point.Next = nearestNext; PathNodeCount++; if (nearestNext == null) { LatestPathNode = point; } else { nearestNext.Previous = point; } // Add shape between nearest and it's duplicate. nearest.Next = node; node.Previous = nearest; lastOfShape.Next = point; } // This return isn't quite correct, but it's a safety measure. // It's because we may have further holes, but they will test if their inside this one. return; //break; } } } }
/// <summary>Converts this path to straight lines only. /// Accuracy is the approx average length of each line segment.</summary> public void ToStraightLines(float accuracy) { if (Width == 0f) { // Calc lengths etc: RecalculateBounds(); } MoveToPoint prevMoveTo = null; VectorPoint point = FirstPathNode; while (point != null) { // If it's straight/ a MoveTo, skip: if (point.IsCurve) { VectorLine line = point as VectorLine; // Replace it with n line segments: int segmentCount = (int)(line.Length / accuracy); if (segmentCount < 1) { segmentCount = 1; } // Setup: float delta = 1f / (float)segmentCount; float progress = delta; // Sample it segmentCount times: VectorPoint previous = point.Previous; for (int i = 0; i < segmentCount; i++) { float x; float y; line.SampleAt(progress, out x, out y); // Create line segment: StraightLinePoint slp = new StraightLinePoint(x, y); slp.Previous = previous; if (previous == null) { FirstPathNode = slp; } else { previous.Next = slp; } previous = slp; progress += delta; } // Increase node count: PathNodeCount += segmentCount - 1; // Link up after too: if (point.Next == null) { LatestPathNode = previous; } else { point.Next.Previous = previous; } if (point.IsClose) { previous.IsClose = true; if (prevMoveTo != null) { prevMoveTo.ClosePoint = previous; } } } else if (point is MoveToPoint) { prevMoveTo = point as MoveToPoint; } // Next one: point = point.Next; } // Recalc: RecalculateBounds(); }