/// <summary> /// Inflate (dilate) the loop by a certain amount. /// </summary> /// <param name="amt">The distance to dialate the path contents.</param> /// <remarks>Use a negative amt value to deflate/erode.</remarks> public void Inflate(float amt) { Dictionary <BNode, InflationCache> cachedInf = new Dictionary <BNode, InflationCache>(); // Go through all nodes and get their influences. We can't do this // on the same pass we update them, or else we would be modifying // values that would be evaluated later as dirty neighbors. foreach (BNode bn in this.nodes) { InflationCache ic = new InflationCache(); bn.GetInflateDirection(out ic.selfInf, out ic.inInf, out ic.outInf); cachedInf.Add(bn, ic); } foreach (KeyValuePair <BNode, InflationCache> kvp in cachedInf) { BNode bn = kvp.Key; // This is just us being lazy for sanity. Sure we could try to // inflate while keeping smooth or symmetry, or it might just // naturally work itself out if we leave it alone - but I'd rather // take the easy way out on this for now. // (wleu) if (bn.tangentMode != BNode.TangentMode.Disconnected) { bn.SetTangentDisconnected(); } bn.Pos += amt * kvp.Value.selfInf; bn.TanIn += amt * (kvp.Value.inInf - kvp.Value.selfInf); bn.TanOut += amt * (kvp.Value.outInf - kvp.Value.selfInf); } }
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); } } } }