public static void TraceDifference( BLoop loopA, BLoop loopB, BLoop loopInto, bool removeInputs = true) { List <BNode> islsA = loopA.GetIslands(IslandTypeRequest.Closed); List <BNode> islsB = loopB.GetIslands(IslandTypeRequest.Closed); TraceDifference(islsA, islsB, loopInto, removeInputs); }
public static void TraceIntersection( BLoop loopA, BLoop loopB, BLoop loopInto, List <BNode> outShapes, bool removeInputs = true) { List <BNode> islsA = loopA.GetIslands(IslandTypeRequest.Closed); List <BNode> islsB = loopB.GetIslands(IslandTypeRequest.Closed); TraceIntersection(islsA, islsB, loopInto, outShapes, removeInputs); }
/// <summary> /// Performs a generic per-island operation between the islands contained in two loops. /// </summary> /// <remarks>Not a generic function - only meant for specific boolean reflow operations.</remarks> /// <param name="left">The collection of islands for the operation.</param> /// <param name="right">The other collection of islands for the operation.</param> /// <param name="op">Function delegate containing the boolean operation.</param> /// <param name="onIslA">An output node that exists on the remaining path(s).</param> /// <param name="removeRight">If true, remove the contents of the right loop parameter after /// the operation.</param> public static void PerIslandBoolean(BLoop left, BLoop right, BooleanImpl op, out BNode onIslA, bool removeRight) { if (left == right || left == null || right == null) { onIslA = left.nodes[0]; return; } onIslA = null; // If we're doing a boolean, it's no longer a generated shape - even if it ends // up untouched. if (left.shape != null) { left.shape.shapeGenerator = null; } right.shape = null; List <BNode> islandsA = left.GetIslands(IslandTypeRequest.Closed); List <BNode> islandB = right.GetIslands(IslandTypeRequest.Closed); foreach (BNode islA in islandsA) { onIslA = islA; foreach (BNode islB in islandB) { List <BNode> islandSegsA = new List <BNode>(islA.Travel()); List <BNode> islandSegsB = new List <BNode>(islB.Travel()); op(left, islandSegsA, islandSegsB, out onIslA); } } if (removeRight == true) { RemoveLoop(right, true); } }
/// <summary> /// Given two loops, calculate information on where they collide with each other. /// /// Does not handle self intersections. /// </summary> /// <param name="loopA">The loop containing the geometry for the left path.</param> /// <param name="loopB">The loop containing the geometry for the right path.</param> /// <returns>The points where segments from the left path and the right path intersect /// each other.</returns> /// <remarks>The terms "left" path and "right" path do not specifically refer to left and right, /// but instead are used to different the two paths from each other.</remarks> public static List <Utils.BezierSubdivSample> GetLoopCollisionInfo( BLoop loopA, BLoop loopB) { List <BNode> islandsA = loopA.GetIslands(); List <BNode> islandsB = loopB.GetIslands(); List <Utils.BezierSubdivSample> collisions = new List <Utils.BezierSubdivSample>(); foreach (BNode isA in islandsA) { BNode.EndpointQuery eqA = isA.GetPathLeftmost(); // Only closed loops count if (eqA.result == BNode.EndpointResult.SuccessfulEdge) { continue; } List <BNode> segsA = new List <BNode>(eqA.Enumerate()); foreach (BNode isB in islandsB) { BNode.EndpointQuery eqB = isB.GetPathLeftmost(); // Only closed loops count if (eqB.result == BNode.EndpointResult.SuccessfulEdge) { continue; } List <BNode> segsB = new List <BNode>(eqB.Enumerate()); GetLoopCollisionInfo(segsA, segsB, collisions); } } Utils.BezierSubdivSample.CleanIntersectionList(collisions); return(collisions); }
/// <summary> /// Perform an intersection operation between two islands using a reflow strategy. /// </summary> /// <param name="left">The collection of islands for the operation.</param> /// <param name="right">The other collection of islands for the operation.</param> /// <param name="onIslA">An output node that exists on the remaining path(s).</param> /// <param name="removeRight">If true, remove the contents of the right loop parameter after /// the operation.</param> public static void Intersection(BLoop left, BLoop right, out BNode onIslA, bool removeRight) { onIslA = null; // Sanity check on geometry, try to union each loop with its islands // so there's no overlapping regions within them. List <BNode> rightIslands = right.GetIslands(); if (rightIslands.Count >= 2) { for (int i = 1; i < rightIslands.Count; ++i) { List <BNode> RSegsA = new List <BNode>(rightIslands[0].Travel()); List <BNode> RSegsB = new List <BNode>(rightIslands[i].Travel()); Union(right, RSegsA, RSegsB, out onIslA, true); } } List <BNode> leftIslands = left.GetIslands(); if (leftIslands.Count >= 2) { for (int i = 1; i < leftIslands.Count; ++i) { List <BNode> LSegsA = new List <BNode>(leftIslands[0].Travel()); List <BNode> LSegsB = new List <BNode>(leftIslands[i].Travel()); Union(right, LSegsA, LSegsB, out onIslA, true); } } leftIslands = left.GetIslands(); rightIslands = right.GetIslands(); foreach (BNode leftIsl in leftIslands) { List <BNode> leftIslandSegs = new List <BNode>(leftIsl.Travel()); foreach (BNode rightIsl in rightIslands) { List <BNode> rightIslandSegs = new List <BNode>(rightIsl.Travel()); Intersection( left, leftIslandSegs, rightIslandSegs, out onIslA); } } foreach (BNode bn in leftIslands) { bn.RemoveIsland(false); } foreach (BNode bn in rightIslands) { bn.RemoveIsland(false); } // TODO: For each island left, we need to see if there's // any shapes being fully contained by the other side. if (removeRight == true) { right.Clear(); RemoveLoop(right, true); } }
public static void Edgify(BLoop loop, float pushOut, float pullIn = 0.0f) { if (pushOut == 0.0f && pullIn == 0.0f) { return; } List <BNode> islands = loop.GetIslands(); foreach (BNode bisl in islands) { // This will probably just give us bisl back, but it that's the case, then it should // be minimal overhead - just to be safe though, and to see what kind of connectivity we're dealing with. BNode.EndpointQuery eq = bisl.GetPathLeftmost(); List <BNode> origs = new List <BNode>(); List <BNode> copies = new List <BNode>(); List <InflationCache> inflations = new List <InflationCache>(); foreach (BNode it in eq.Enumerate()) { origs.Add(it); BNode cpy = new BNode(loop, it, false, true); copies.Add(cpy); loop.nodes.Add(cpy); InflationCache ic = new InflationCache(); it.GetInflateDirection(out ic.selfInf, out ic.inInf, out ic.outInf); inflations.Add(ic); } // Stitch the new chain - it should have a reverse winding. // // The loop is a little backwards, but basically we sub instead of add to // treat the prev item in the array like the next in the chain. for (int i = 1; i < copies.Count; ++i) { copies[i].next = copies[i - 1]; copies[i - 1].prev = copies[i]; } int lastIdx = copies.Count - 1; if (eq.result == BNode.EndpointResult.Cyclical) { // If it was cyclical, it should close in on itself and it should // never touch the original outline; // // Remember we're treating copies in reverse. copies[lastIdx].prev = copies[0]; copies[0].next = copies[lastIdx]; } else { // Or else the opposite ends connect to each other. // Remember we're treating copies in reverse. origs[0].prev = copies[0]; copies[0].next = origs[0]; origs[lastIdx].next = copies[lastIdx]; copies[lastIdx].prev = origs[lastIdx]; origs[0].UseTanIn = false; origs[lastIdx].UseTanOut = false; copies[0].UseTanOut = false; copies[lastIdx].UseTanIn = false; } if (pushOut != 0.0f) { // Now that we have copies and connectivity set up, it's time // to apply the thickening for (int i = 0; i < origs.Count; ++i) { // Push out the original origs[i].Pos += pushOut * inflations[i].selfInf; origs[i].TanIn += pushOut * (inflations[i].inInf - inflations[i].selfInf); origs[i].TanOut += pushOut * (inflations[i].outInf - inflations[i].selfInf); } } if (pullIn != 0.0f) { // We can optionally pull in the copy for (int i = 0; i < copies.Count; ++i) { copies[i].Pos += pullIn * inflations[i].selfInf; copies[i].TanIn += pullIn * (inflations[i].inInf - inflations[i].selfInf); copies[i].TanOut += pullIn * (inflations[i].outInf - inflations[i].selfInf); } } } }