/// <summary> /// Render the tree data of the document, and render the outline and preview to the Scene view. /// </summary> private void OnSceneGUI() { BernyTest t = (BernyTest)this.target; if (t == null || t.curveDocument == null) { return; } Handles.BeginGUI(); foreach (BNode selbn in this.selectedNodes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR int parentID = (selbn.parent != null) ? selbn.parent.debugCounter : -1; GUILayout.Label($"Selected: {selbn.debugCounter.ToString()} - {selbn.tangentMode.ToString()} - {selbn.Pos.x} : {selbn.Pos.y} - [Parent: {parentID}]"); #else GUILayout.Label($"Selected: {selbn.tangentMode.ToSTring()} - {selbn.Pos.x} : {selbn.Pos.y}"); #endif } this.DoUIDocument(t.curveDocument, 0.0f); if (this.showCurveIDs == true && Camera.current != null) { foreach (BNode bn in t.curveDocument.EnumerateNodes()) { Vector2 v2 = Camera.current.WorldToScreenPoint(bn.Pos); GUI.Label(new Rect(v2.x + 10.0f, Screen.height - v2.y - 90, 100.0f, 100.0f), bn.debugCounter.ToString()); } } Handles.EndGUI(); bool recalculateAverage = false; for (int i = 0; i < this.intersectionPreviews.Count; ++i) { Handles.Button( this.intersectionPreviews[i], Quaternion.identity, 0.1f, 0.1f, Handles.SphereHandleCap); } //Handles.DrawLine(this.intersectTestStart, this.intersectTextEnd); // Draw all curves and show handles for them. Handles.color = Color.white; foreach (BNode bn in t.curveDocument.EnumerateNodes()) { bool sel = this.selectedNodes.Contains(bn); if (sel == true) { Handles.color = Color.green; } else { Handles.color = Color.white; } if (drawKnots == true) { bool inter = Handles.Button(bn.Pos, Quaternion.identity, 0.02f, 0.02f, Handles.CubeHandleCap); Handles.color = Color.white; if (inter == true) { recalculateAverage = true; if (Event.current.shift == true) { if (sel == true) { this.selectedNodes.Remove(bn); } else { this.selectedNodes.Add(bn); } } else { this.selectedNodes.Clear(); this.selectedNodes.Add(bn); } this.movedTangent = null; } } BSample bit = bn.sample; BSample bitFirst = bit; while (bit != null && bit.parent == bn && bit.next != null) { Handles.DrawLine(bit.pos, bit.next.pos); bit = bit.next; if (bit == bitFirst) { break; } } if (this.showCurveIDs == true && bn.next != null) { Vector2 tan = bn.GetTangent(BNode.TangentType.Input); if (tan.sqrMagnitude < Mathf.Epsilon) { continue; } float ang = Mathf.Atan2(tan.y, tan.x); float angS1 = ang - 0.2f; Vector2 vang1 = new Vector2(Mathf.Cos(angS1), Mathf.Sin(angS1)) * 0.3f; float angS2 = ang + 0.2f; Vector2 vang2 = new Vector2(Mathf.Cos(angS2), Mathf.Sin(angS2)) * 0.3f; Handles.DrawLine(bn.Pos, bn.Pos + vang1); Handles.DrawLine(bn.Pos, bn.Pos + vang2); } } // For selected items, show handles for their tangents. if (drawKnots == true) { foreach (BNode bn in this.selectedNodes) { if (bn.UseTanIn == true) { Handles.color = Color.blue; if (Handles.Button(bn.Pos + bn.TanIn, Quaternion.identity, 0.01f, 0.01f, Handles.CubeHandleCap) == true) { this.movedTangent = bn; this.movedTangentType = BNode.TangentType.Input; } Handles.color = Color.white; Handles.DrawDottedLine(bn.Pos + bn.TanIn, bn.Pos, 1.0f); } // if (bn.UseTanOut == true) { Handles.color = Color.blue; if (Handles.Button(bn.Pos + bn.TanOut, Quaternion.identity, 0.01f, 0.01f, Handles.CubeHandleCap) == true) { this.movedTangent = bn; this.movedTangentType = BNode.TangentType.Output; } Handles.color = Color.white; Handles.DrawDottedLine(bn.Pos + bn.TanOut, bn.Pos, 1.0f); } } } if (this.movedTangent != null) { Vector3 oriPos = (this.movedTangentType == BNode.TangentType.Input) ? this.movedTangent.TanIn : this.movedTangent.TanOut; oriPos += (Vector3)this.movedTangent.Pos; Vector3 tanPos = Handles.PositionHandle(oriPos, Quaternion.identity); if (tanPos != oriPos) { Vector2 newTan = (Vector2)tanPos - this.movedTangent.Pos; if (this.movedTangentType == BNode.TangentType.Input) { this.movedTangent.TanIn = newTan; } else { this.movedTangent.TanOut = newTan; } this.movedTangent.FlagDirty(); } } if (recalculateAverage == true) { this.RecalculateSelectionCentroid(); } if (this.selectedNodes.Count > 0) { Vector3 origAvg = this.averagePoint; Vector3 mod = Handles.PositionHandle(origAvg, Quaternion.identity); if (origAvg != mod) { Vector2 diff = mod - origAvg; this.averagePoint = mod; foreach (BNode bn in this.selectedNodes) { bn.Pos += diff; bn.FlagDirty(); } } } if (t.curveDocument.IsDirty() == true) { t.curveDocument.FlushDirty(); } }
/// <summary> /// Unity inspector function. /// </summary> public override void OnInspectorGUI() { if (drawKnots == false) { this.selectedNodes.Clear(); } base.OnInspectorGUI(); drawKnots = EditorGUILayout.Toggle("Draw Knots", drawKnots); BernyTest t = (BernyTest)this.target; if (t == null || t.curveDocument == null) { return; } this.showCurveIDs = GUILayout.Toggle(this.showCurveIDs, "Show Curve IDs"); if (GUILayout.Button("Test Validity") == true) { t.curveDocument.TestValidity(); } if (GUILayout.Button("Fill") == true) { t.UpdateFillsForAll(BernyTest.FillType.Filled, this.strokeWidth); } if (GUILayout.Button("Fill Outline") == true) { t.UpdateFillsForAll(BernyTest.FillType.Outlined, this.strokeWidth); } if (GUILayout.Button("Fill Outlined") == true) { t.UpdateFillsForAll(BernyTest.FillType.FilledAndOutlined, this.strokeWidth); } this.strokeWidth = EditorGUILayout.Slider("Stroke Width", this.strokeWidth, 0.001f, 1.0f); GUILayout.BeginHorizontal(); GUI.color = Color.green; if (GUILayout.Button("Select All") == true) { this.selectedNodes = new HashSet <BNode>(t.curveDocument.EnumerateNodes()); } GUI.color = Color.white; if (GUILayout.Button("Deselect All") == true) { this.selectedNodes.Clear(); } GUILayout.EndHorizontal(); if (GUILayout.Button("Scan Selected Intersections") == true) { this.ScanSelectedIntersections(t.curveDocument); } GUILayout.Space(20.0f); string [] files = new string[] { "Ven", "TriVen", "CircAnCirc", "Complex", "Complex2", "Edges", "ShapeCircle", "ShapeEllipse", "ShapeLine", "ShapePolygon", "ShapePolyline", "ShapeRect" }; foreach (string f in files) { GUILayout.BeginHorizontal(); if (GUILayout.Button("LOAD " + f) == true) { t.curveDocument.Clear(); SVGSerializer.Load("TestSamples/" + f + ".svg", t.curveDocument, true); t.curveDocument.FlushDirty(); } if (GUILayout.Button("...", GUILayout.Width(30.0f)) == true) { System.Diagnostics.Process.Start("TestSamples\\" + f + ".svg"); } GUILayout.EndHorizontal(); } GUILayout.Space(20.0f); this.infAmt = EditorGUILayout.FloatField("Inflation Amt", this.infAmt); if (GUILayout.Button("Inflate") == true) { foreach (Layer l in t.curveDocument.Layers()) { foreach (BShape bs in l.shapes) { foreach (BLoop bl in bs.loops) { bl.Deinflect(); bl.Inflate(this.infAmt); } } } } if (GUILayout.Button("Edgeify") == true) { foreach (BLoop bl in t.curveDocument.EnumerateLoops()) { bl.Deinflect(); PxPre.Berny.Operators.Edgify(bl, this.infAmt); } } GUILayout.Space(20.0f); GUI.color = Color.red; if (GUILayout.Button("Clear") == true) { t.curveDocument.Clear(); t.ClearFills(); } GUI.color = Color.white; if (GUILayout.Button("Save SVG") == true) { SVGSerializer.Save("TestSave.svg", t.curveDocument); } if (GUILayout.Button("Load SVG") == true) { t.curveDocument.Clear(); SVGSerializer.Load("TestSave.svg", t.curveDocument); } GUILayout.Space(20.0f); if (this.selectedNodes.Count > 0) { if (GUILayout.Button("Reverse Windings") == true) { HashSet <BLoop> loops = new HashSet <BLoop>(); foreach (BNode bn in this.selectedNodes) { loops.Add(bn.parent); } foreach (BLoop loop in loops) { if (loop == null) { continue; } loop.Reverse(); } } GUI.color = Color.red; GUILayout.BeginHorizontal(); if (GUILayout.Button("Delete Selected") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.parent.RemoveNode(selNode); } this.selectedNodes.Clear(); this.movedTangent = null; } if (GUILayout.Button("Disconnect Selected") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.Disconnect(true); } } if (GUILayout.Button("Detach Selected") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.Detach(); } } GUILayout.EndHorizontal(); GUI.color = Color.white; if (GUILayout.Button("Expand Islands") == true) { HashSet <BNode> toScan = new HashSet <BNode>(this.selectedNodes); while (toScan.Count > 0) { BNode bnCut = Utils.GetFirstInHash(toScan); foreach (BNode bnT in bnCut.Travel()) { toScan.Remove(bnT); this.selectedNodes.Add(bnT); } } } if (GUILayout.Button("Connect") == true) { if (this.selectedNodes.Count != 2) { return; } List <BNode> selList = new List <BNode>(this.selectedNodes); selList[0].parent.ConnectNodes(selList[0], selList[1]); } if (GUILayout.Button("Subdivide Selected") == true) { HashSet <BNode> subbed = new HashSet <BNode>(); foreach (BNode selNode in this.selectedNodes) { BNode bsubed = selNode.Subdivide(0.5f); if (bsubed != null) { subbed.Add(bsubed); } } if (subbed.Count > 0) { foreach (BNode bn in subbed) { this.selectedNodes.Add(bn); } this.RecalculateSelectionCentroid(); } } GUILayout.BeginHorizontal(); if (GUILayout.Button("Round Selected") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.Round(); } } if (GUILayout.Button("Smooth") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.SetTangentSmooth(); } } if (GUILayout.Button("Symmetrize") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.SetTangentsSymmetry(); } } if (GUILayout.Button("Disconnect") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.SetTangentDisconnected(); } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Enable Inputs") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.UseTanIn = true; } } if (GUILayout.Button("Enable Outputs") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.UseTanOut = true; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Disable Inputs") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.UseTanIn = false; } } if (GUILayout.Button("Disable Outputs") == true) { foreach (BNode selNode in this.selectedNodes) { selNode.UseTanOut = false; } } GUILayout.EndHorizontal(); if (GUILayout.Button("Islands") == true) { HashSet <BLoop> foundLoops = new HashSet <BLoop>(); foreach (BNode bn in this.selectedNodes) { foundLoops.Add(bn.parent); } foreach (BLoop bl in foundLoops) { int island = bl.CalculateIslands(); for (int i = 0; i < island - 1; ++i) { bl.ExtractIsland(bl.nodes[0]); } } } GUILayout.BeginHorizontal(); if (GUILayout.Button("Winding Simple") == true) { if (this.selectedNodes.Count > 0) { BNode bn = Utils.GetFirstInHash(this.selectedNodes); float w = bn.parent.CalculateWindingSimple(bn, true); Debug.Log("Simple winding of " + w.ToString()); } } if (GUILayout.Button("Winding Samples") == true) { BNode bn = Utils.GetFirstInHash(this.selectedNodes); float w = bn.parent.CalculateWindingSamples(bn, true); Debug.Log("Simple winding of " + w.ToString()); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Calculate ArcLength") == true) { HashSet <BLoop> loops = new HashSet <BLoop>(); foreach (BNode bn in this.selectedNodes) { loops.Add(bn.parent); } foreach (BLoop loop in loops) { float len = loop.CalculateArclen(); Debug.Log("Calculated arclen of " + len.ToString()); } } if (GUILayout.Button("Calculate SampleLen") == true) { HashSet <BLoop> loops = new HashSet <BLoop>(); foreach (BNode bn in this.selectedNodes) { loops.Add(bn.parent); } foreach (BLoop loop in loops) { float len = loop.CalculateSampleLens(); Debug.Log("Calculated sample len of " + len.ToString()); } } GUILayout.EndHorizontal(); if (GUILayout.Button("Calculate BBoxes") == true) { BoundsMM2 total = BoundsMM2.GetInifiniteRegion(); foreach (BNode bn in this.selectedNodes) { if (bn.next == null) { continue; } BoundsMM2 b2 = bn.GetBounds(); Debug.Log($"Node found to have bounds of region min {{{b2.min.x}, {b2.min.y}}} - and max {{{b2.max.x}, {b2.max.y} }}"); total.Union(b2); } Debug.Log($"Total selected bounds of region min {{{total.min.x}, {total.min.y}}} - and max {{{total.max.x}, {total.max.y} }}"); } if (GUILayout.Button("Split into Thirds") == true) { Vector2 pt0, pt1, pt2, pt3; foreach (BNode bn in this.selectedNodes) { if (bn.next == null) { continue; } Utils.SubdivideBezier(bn.Pos, bn.Pos + bn.TanOut, bn.next.Pos + bn.next.TanIn, bn.next.Pos, out pt0, out pt1, out pt2, out pt3, 0.1f, 0.9f); bn.Pos = pt0; bn.TanOut = (pt1 - pt0); bn.next.TanIn = (pt2 - pt3); bn.next.Pos = pt3; } } GUILayout.Space(20.0f); this.intersectTestStart = EditorGUILayout.Vector2Field("Intersection Start", this.intersectTestStart); this.intersectTextEnd = EditorGUILayout.Vector2Field("Intersection End", this.intersectTextEnd); if (GUILayout.Button("Line Intersection Test") == true) { this.intersectionPreviews.Clear(); foreach (BNode node in this.selectedNodes) { List <float> curveOuts = new List <float>(); if (node.next == null) { continue; } BNode.PathBridge pb = node.GetPathBridgeInfo(); Vector2 pt0 = node.Pos; Vector2 pt1 = node.Pos + pb.prevTanOut; Vector2 pt2 = node.next.Pos + pb.nextTanIn; Vector2 pt3 = node.next.Pos; int cols = Utils.IntersectLine( curveOuts, null, pt0, pt1, pt2, pt3, this.intersectTestStart, this.intersectTextEnd); for (int i = 0; i < cols; ++i) { float intLam = curveOuts[i]; float a, b, c, d; Utils.GetBezierWeights(intLam, out a, out b, out c, out d); this.intersectionPreviews.Add(a * pt0 + b * pt1 + c * pt2 + d * pt3); } } if (this.intersectionPreviews.Count == 0) { Debug.Log("No collisions found"); } else { Debug.Log($"{this.intersectionPreviews.Count} Collisions found"); } } GUILayout.BeginHorizontal(); if (GUILayout.Button("Wind Sel Back") == true) { List <BNode> bnsel = new List <BNode>(this.selectedNodes); this.selectedNodes.Clear(); foreach (BNode bn in bnsel) { if (bn.prev != null) { this.selectedNodes.Add(bn.prev); } } } if (GUILayout.Button("Wind Sel Fwd") == true) { List <BNode> bnsel = new List <BNode>(this.selectedNodes); this.selectedNodes.Clear(); foreach (BNode bn in bnsel) { if (bn.next != null) { this.selectedNodes.Add(bn.next); } } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Test Union") == true) { BLoop srcLoop = null; List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(out srcLoop, this.selectedNodes); // If loops is filled, srcLoop should be non-null if (loops.Count > 0) { BNode filler; Boolean.Union(srcLoop, out filler, loops.ToArray()); } } if (GUILayout.Button("Union Trace") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); Boolean.TraceUnion(loops, loops[0], null, true); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Test Difference") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); if (loops.Count >= 2) { BNode filler; Boolean.Difference(loops[0], loops[1], out filler); } } if (GUILayout.Button("Difference Trace") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); if (loops.Count >= 2) { Boolean.TraceDifference( loops[0], loops[1], loops[0], true); } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Test Intersection") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); if (loops.Count >= 2) { BNode filler; Boolean.Intersection(loops[0], loops[1], out filler, true); } } if (GUILayout.Button("Intersection Trace") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); if (loops.Count >= 2) { Boolean.TraceIntersection( loops[0], loops[1], loops[0], null, true); } } GUILayout.EndHorizontal(); if (GUILayout.Button("Test Exclusion") == true) { List <BLoop> loops = Boolean.GetUniqueLoopsInEncounteredOrder(this.selectedNodes); if (loops.Count >= 2) { Boolean.Exclusion(loops[0], loops[1], true); } } if (GUILayout.Button("Bridge") == true) { HashSet <BLoop> loops = new HashSet <BLoop>(); List <BLoop> ol = new List <BLoop>(); foreach (BNode bn in this.selectedNodes) { if (loops.Add(bn.parent) == true) { ol.Add(bn.parent); } } if (ol.Count > 0) { BLoop blTarg = ol[0]; if (ol.Count >= 2) { //Boolean.Union(blTarg, ol[1]); ol[1].DumpInto(blTarg); } List <BNode> islands = blTarg.GetIslands(IslandTypeRequest.Closed); if (islands.Count >= 2) { List <BNode> segmentsA = new List <BNode>(islands[0].Travel()); List <BNode> segmentsB = new List <BNode>(islands[1].Travel()); BNode innerBridgeSeg, outerBridgeSeg; float innerBridgeT, outerBridgeT; BNode.FindBridge( segmentsA, segmentsB, out innerBridgeSeg, out outerBridgeSeg, out innerBridgeT, out outerBridgeT); if (outerBridgeSeg != null) { // We have what we need for a connection. BNode.MakeBridge(innerBridgeSeg, innerBridgeT, outerBridgeSeg, outerBridgeT); } } } } } textToCreate = GUILayout.TextField( textToCreate, GUILayout.ExpandWidth(true), GUILayout.Height(100.0f)); bridgeFonts = GUILayout.Toggle(bridgeFonts, "Bridge Font"); if (GUILayout.Button("Create Text") == true) { List <BShape> charShapes = PxPre.Berny.Text.GenerateString( t.curveDocument.GetFirstLayer(), Vector2.zero, t.typeface, 1.0f, textToCreate); if (bridgeFonts == true) { foreach (BShape bs in charShapes) { Text.BridgeGlyph(bs); } } } if (GUILayout.Button("Bridge Font Shapes") == true) { foreach (BShape shape in t.curveDocument.EnumerateShapes()) { Text.BridgeGlyph(shape); } } if (GUILayout.Button("Clean") == true) { t.curveDocument.Clean(); } if (t.curveDocument.IsDirty() == true) { t.curveDocument.FlushDirty(); } }