From 94a20083cdb2ff893ad419fb5f4f731031ea157e Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 19 Feb 2025 08:04:01 -0700 Subject: [PATCH 1/8] save --- d2graph/cyclediagram.go | 7 + d2graph/{grid_diagram.go => griddiagram.go} | 0 d2graph/{seqdiagram.go => sequencediagram.go} | 0 d2layouts/d2cycle/layout.go | 241 ++++++ d2layouts/d2layouts.go | 11 +- d2renderers/d2svg/d2svg.go | 90 ++- d2target/d2target.go | 3 + .../txtar/cycle-diagram/dagre/board.exp.json | 694 ++++++++++++++++++ .../txtar/cycle-diagram/dagre/sketch.exp.svg | 98 +++ .../txtar/cycle-diagram/elk/board.exp.json | 694 ++++++++++++++++++ .../txtar/cycle-diagram/elk/sketch.exp.svg | 98 +++ e2etests/txtar.txt | 4 + lib/geo/point.go | 9 + 13 files changed, 1928 insertions(+), 21 deletions(-) create mode 100644 d2graph/cyclediagram.go rename d2graph/{grid_diagram.go => griddiagram.go} (100%) rename d2graph/{seqdiagram.go => sequencediagram.go} (100%) create mode 100644 d2layouts/d2cycle/layout.go create mode 100644 e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg diff --git a/d2graph/cyclediagram.go b/d2graph/cyclediagram.go new file mode 100644 index 0000000000..a8f8e0b1ad --- /dev/null +++ b/d2graph/cyclediagram.go @@ -0,0 +1,7 @@ +package d2graph + +import "oss.terrastruct.com/d2/d2target" + +func (obj *Object) IsCycleDiagram() bool { + return obj != nil && obj.Shape.Value == d2target.ShapeCycleDiagram +} diff --git a/d2graph/grid_diagram.go b/d2graph/griddiagram.go similarity index 100% rename from d2graph/grid_diagram.go rename to d2graph/griddiagram.go diff --git a/d2graph/seqdiagram.go b/d2graph/sequencediagram.go similarity index 100% rename from d2graph/seqdiagram.go rename to d2graph/sequencediagram.go diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go new file mode 100644 index 0000000000..d957c68142 --- /dev/null +++ b/d2layouts/d2cycle/layout.go @@ -0,0 +1,241 @@ +package d2cycle + +import ( + "context" + "math" + + "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/lib/geo" + "oss.terrastruct.com/d2/lib/label" + "oss.terrastruct.com/util-go/go2" +) + +const ( + MIN_RADIUS = 200 + PADDING = 20 + MIN_SEGMENT_LEN = 10 + ARC_STEPS = 30 // high resolution for smooth arcs + +) + +// Layout arranges nodes in a circle, ensures label/icon positions are set, +// then routes edges with arcs that get clipped at node borders. +func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error { + objects := g.Root.ChildrenArray + if len(objects) == 0 { + return nil + } + + // Make sure every object that has label/icon also has a default position + for _, obj := range g.Objects { + positionLabelsIcons(obj) + } + + // Arrange objects in a circle + radius := calculateRadius(objects) + positionObjects(objects, radius) + + // Create arcs + for _, edge := range g.Edges { + createCircularArc(edge) + } + + return nil +} + +func calculateRadius(objects []*d2graph.Object) float64 { + numObjects := float64(len(objects)) + maxSize := 0.0 + for _, obj := range objects { + size := math.Max(obj.Box.Width, obj.Box.Height) + maxSize = math.Max(maxSize, size) + } + // ensure enough radius to fit all objects + minRadius := (maxSize/2.0 + PADDING) / math.Sin(math.Pi/numObjects) + return math.Max(minRadius, MIN_RADIUS) +} + +func positionObjects(objects []*d2graph.Object, radius float64) { + numObjects := float64(len(objects)) + // Offset so i=0 is top-center + angleOffset := -math.Pi / 2 + + for i, obj := range objects { + angle := angleOffset + (2 * math.Pi * float64(i) / numObjects) + + x := radius * math.Cos(angle) + y := radius * math.Sin(angle) + + // center the box at (x, y) + obj.TopLeft = geo.NewPoint( + x-obj.Box.Width/2, + y-obj.Box.Height/2, + ) + } +} + +// createCircularArc samples a smooth arc from center to center, then +// forces the endpoints onto each shape's border, and finally calls +// TraceToShape to clip any additional overrun. +func createCircularArc(edge *d2graph.Edge) { + if edge.Src == nil || edge.Dst == nil { + return + } + + srcCenter := edge.Src.Center() + dstCenter := edge.Dst.Center() + + // angles from origin + srcAngle := math.Atan2(srcCenter.Y, srcCenter.X) + dstAngle := math.Atan2(dstCenter.Y, dstCenter.X) + if dstAngle < srcAngle { + dstAngle += 2 * math.Pi + } + + arcRadius := math.Hypot(srcCenter.X, srcCenter.Y) + + // Sample points along the arc + path := make([]*geo.Point, 0, ARC_STEPS+1) + for i := 0; i <= ARC_STEPS; i++ { + t := float64(i) / float64(ARC_STEPS) + angle := srcAngle + t*(dstAngle-srcAngle) + x := arcRadius * math.Cos(angle) + y := arcRadius * math.Sin(angle) + path = append(path, geo.NewPoint(x, y)) + } + // Set start/end to exact centers + path[0] = srcCenter + path[len(path)-1] = dstCenter + + // Use TraceToShape to clip route to node borders + edge.Route = path + startIndex, endIndex := edge.TraceToShape(edge.Route, 0, len(edge.Route)-1) + if startIndex < endIndex { + edge.Route = edge.Route[startIndex : endIndex+1] + } + edge.IsCurve = true +} + +// clampPointOutsideBox walks forward from 'startIdx' until the path segment +// leaves the bounding box. Then it sets path[startIdx] to the intersection. +// If we never find it, we return (startIdx, path[startIdx]) meaning we can't clamp. +func clampPointOutsideBox(box *geo.Box, path []*geo.Point, startIdx int) (int, *geo.Point) { + if startIdx >= len(path)-1 { + return startIdx, path[startIdx] + } + // If path[startIdx] is outside, no clamp needed + if !boxContains(box, path[startIdx]) { + return startIdx, path[startIdx] + } + + // Walk forward looking for outside + for i := startIdx + 1; i < len(path); i++ { + insideNext := boxContains(box, path[i]) + if insideNext { + // still inside -> keep going + continue + } + // crossing from inside to outside between path[i-1], path[i] + seg := geo.NewSegment(path[i-1], path[i]) + inters := boxIntersections(box, *seg) + if len(inters) > 0 { + // use first intersection + return i, inters[0] + } + // fallback => no intersection found + return i, path[i] + } + // entire remainder is inside, so we can't clamp + // Just return the end + last := len(path) - 1 + return last, path[last] +} + +// clampPointOutsideBoxReverse scans backward from endIdx while path[j] is in the box. +// Once we find crossing (outside→inside), we return (j, intersection). +func clampPointOutsideBoxReverse(box *geo.Box, path []*geo.Point, endIdx int) (int, *geo.Point) { + if endIdx <= 0 { + return endIdx, path[endIdx] + } + if !boxContains(box, path[endIdx]) { + // already outside + return endIdx, path[endIdx] + } + + for j := endIdx - 1; j >= 0; j-- { + if boxContains(box, path[j]) { + continue + } + // crossing from outside -> inside between path[j], path[j+1] + seg := geo.NewSegment(path[j], path[j+1]) + inters := boxIntersections(box, *seg) + if len(inters) > 0 { + return j, inters[0] + } + return j, path[j] + } + + // entire path inside + return 0, path[0] +} + +// Helper if your geo.Box doesn’t implement Contains() +func boxContains(b *geo.Box, p *geo.Point) bool { + // typical bounding-box check + return p.X >= b.TopLeft.X && + p.X <= b.TopLeft.X+b.Width && + p.Y >= b.TopLeft.Y && + p.Y <= b.TopLeft.Y+b.Height +} + +// Helper if your geo.Box doesn’t implement Intersections(geo.Segment) yet +func boxIntersections(b *geo.Box, seg geo.Segment) []*geo.Point { + // We'll assume d2's standard geo.Box has a built-in Intersections(*Segment) method. + // If not, implement manually. For example, checking each of the 4 edges: + // left, right, top, bottom + // For simplicity, if you do have b.Intersections(...) you can just do: + // return b.Intersections(seg) + return b.Intersections(seg) + // If you don't have that, you'd code the line-rect intersection yourself. +} + +// positionLabelsIcons is basically your logic that sets default label/icon positions if needed +func positionLabelsIcons(obj *d2graph.Object) { + // If there's an icon but no icon position, give it a default + if obj.Icon != nil && obj.IconPosition == nil { + if len(obj.ChildrenArray) > 0 { + obj.IconPosition = go2.Pointer(label.OutsideTopLeft.String()) + if obj.LabelPosition == nil { + obj.LabelPosition = go2.Pointer(label.OutsideTopRight.String()) + return + } + } else if obj.SQLTable != nil || obj.Class != nil || obj.Language != "" { + obj.IconPosition = go2.Pointer(label.OutsideTopLeft.String()) + } else { + obj.IconPosition = go2.Pointer(label.InsideMiddleCenter.String()) + } + } + + // If there's a label but no label position, give it a default + if obj.HasLabel() && obj.LabelPosition == nil { + if len(obj.ChildrenArray) > 0 { + obj.LabelPosition = go2.Pointer(label.OutsideTopCenter.String()) + } else if obj.HasOutsideBottomLabel() { + obj.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String()) + } else if obj.Icon != nil { + obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String()) + } else { + obj.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String()) + } + + // If the label is bigger than the shape, fallback to outside positions + if float64(obj.LabelDimensions.Width) > obj.Width || + float64(obj.LabelDimensions.Height) > obj.Height { + if len(obj.ChildrenArray) > 0 { + obj.LabelPosition = go2.Pointer(label.OutsideTopCenter.String()) + } else { + obj.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String()) + } + } + } +} diff --git a/d2layouts/d2layouts.go b/d2layouts/d2layouts.go index 87874a6b41..b4eff3b9fc 100644 --- a/d2layouts/d2layouts.go +++ b/d2layouts/d2layouts.go @@ -9,6 +9,7 @@ import ( "strings" "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/d2layouts/d2cycle" "oss.terrastruct.com/d2/d2layouts/d2grid" "oss.terrastruct.com/d2/d2layouts/d2near" "oss.terrastruct.com/d2/d2layouts/d2sequence" @@ -20,12 +21,12 @@ import ( type DiagramType string -// a grid diagram at a constant near is const ( DefaultGraphType DiagramType = "" ConstantNearGraph DiagramType = "constant-near" GridDiagram DiagramType = "grid-diagram" SequenceDiagram DiagramType = "sequence-diagram" + CycleDiagram DiagramType = "cycle-diagram" ) type GraphInfo struct { @@ -260,6 +261,12 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co if err != nil { return err } + case CycleDiagram: + log.Debug(ctx, "layout sequence", slog.Any("rootlevel", g.RootLevel), slog.Any("shapes", g.PrintString())) + err = d2cycle.Layout(ctx, g, coreLayout) + if err != nil { + return err + } default: log.Debug(ctx, "default layout", slog.Any("rootlevel", g.RootLevel), slog.Any("shapes", g.PrintString())) err := coreLayout(ctx, g) @@ -360,6 +367,8 @@ func NestedGraphInfo(obj *d2graph.Object) (gi GraphInfo) { gi.DiagramType = SequenceDiagram } else if obj.IsGridDiagram() { gi.DiagramType = GridDiagram + } else if obj.IsCycleDiagram() { + gi.DiagramType = CycleDiagram } return gi } diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 153b7b3451..a42ee3681b 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -452,37 +452,78 @@ func getArrowheadAdjustments(connection d2target.Connection, idToShape map[strin func pathData(connection d2target.Connection, srcAdj, dstAdj *geo.Point) string { var path []string route := connection.Route + if len(route) == 0 { + return "" + } + // Move command to start path = append(path, fmt.Sprintf("M %f %f", route[0].X+srcAdj.X, route[0].Y+srcAdj.Y, )) if connection.IsCurve { + // If we don't have enough points to do triple-step, handle small fallback + if len(route) < 3 { + // If only 1 or 2 points in route, just draw lines + for _, p := range route[1:] { + path = append(path, fmt.Sprintf("L %f %f", + p.X+dstAdj.X, p.Y+dstAdj.Y, + )) + } + return strings.Join(path, " ") + } + i := 1 - for ; i < len(route)-3; i += 3 { + // Process triple curves in steps of 3 + for ; i+2 < len(route)-1; i += 3 { path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", route[i].X, route[i].Y, route[i+1].X, route[i+1].Y, route[i+2].X, route[i+2].Y, )) } - // final curve target adjustment - path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", - route[i].X, route[i].Y, - route[i+1].X, route[i+1].Y, - route[i+2].X+dstAdj.X, - route[i+2].Y+dstAdj.Y, - )) + + // Now handle the “final” curve to last point + // Make sure i+2 is still within range + if i+2 < len(route) { + // last triple + path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", + route[i].X, route[i].Y, + route[i+1].X, route[i+1].Y, + route[i+2].X+dstAdj.X, // final point plus dst adjustment + route[i+2].Y+dstAdj.Y, + )) + } else if i+1 < len(route) { + // We have i+1 but not i+2 => do a simpler final curve or line + path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", + route[i].X, route[i].Y, + route[i].X, route[i].Y, // repeated for control + route[i+1].X+dstAdj.X, + route[i+1].Y+dstAdj.Y, + )) + } else { + // We have no final triple => do nothing or fallback line + } } else { + // Not a curve => the "rounded corner" logic for i := 1; i < len(route)-1; i++ { prevSource := route[i-1] prevTarget := route[i] currTarget := route[i+1] + + // Make sure i+1 is valid + if i+1 >= len(route) { + break + } + prevVector := prevSource.VectorTo(prevTarget) currVector := prevTarget.VectorTo(currTarget) - dist := geo.EuclideanDistance(prevTarget.X, prevTarget.Y, currTarget.X, currTarget.Y) + dist := geo.EuclideanDistance( + prevTarget.X, prevTarget.Y, + currTarget.X, currTarget.Y, + ) connectionBorderRadius := connection.BorderRadius units := math.Min(connectionBorderRadius, dist/2) @@ -490,20 +531,26 @@ func pathData(connection d2target.Connection, srcAdj, dstAdj *geo.Point) string prevTranslations := prevVector.Unit().Multiply(units).ToPoint() currTranslations := currVector.Unit().Multiply(units).ToPoint() + // Move to corner with "L" path = append(path, fmt.Sprintf("L %f %f", prevTarget.X-prevTranslations.X, prevTarget.Y-prevTranslations.Y, )) - // If the segment length is too small, instead of drawing 2 arcs, just skip this segment and bezier curve to the next one if units < connectionBorderRadius && i < len(route)-2 { + // Next checks i+2 => ensure it’s in range + if i+2 >= len(route) { + // can't do nextTarget => break or do fallback + continue + } nextTarget := route[i+2] - nextVector := geo.NewVector(nextTarget.X-currTarget.X, nextTarget.Y-currTarget.Y) - i++ + nextVector := geo.NewVector( + nextTarget.X-currTarget.X, + nextTarget.Y-currTarget.Y, + ) + i++ // skip next point nextTranslations := nextVector.Unit().Multiply(units).ToPoint() - // These 2 bezier control points aren't just at the corner -- they are reflected at the corner, which causes the curve to be ~tangent to the corner, - // which matches how the two arcs look path = append(path, fmt.Sprintf("C %f %f %f %f %f %f", // Control point prevTarget.X+prevTranslations.X, @@ -511,7 +558,7 @@ func pathData(connection d2target.Connection, srcAdj, dstAdj *geo.Point) string // Control point currTarget.X-nextTranslations.X, currTarget.Y-nextTranslations.Y, - // Where curve ends + // End currTarget.X+nextTranslations.X, currTarget.Y+nextTranslations.Y, )) @@ -525,11 +572,14 @@ func pathData(connection d2target.Connection, srcAdj, dstAdj *geo.Point) string } } - lastPoint := route[len(route)-1] - path = append(path, fmt.Sprintf("L %f %f", - lastPoint.X+dstAdj.X, - lastPoint.Y+dstAdj.Y, - )) + // Finally, draw a line to the last route point + dst offset + if len(route) > 1 { + lastPoint := route[len(route)-1] + path = append(path, fmt.Sprintf("L %f %f", + lastPoint.X+dstAdj.X, + lastPoint.Y+dstAdj.Y, + )) + } } return strings.Join(path, " ") diff --git a/d2target/d2target.go b/d2target/d2target.go index 266e0f184e..68e7eb3313 100644 --- a/d2target/d2target.go +++ b/d2target/d2target.go @@ -942,6 +942,7 @@ const ( ShapeSQLTable = "sql_table" ShapeImage = "image" ShapeSequenceDiagram = "sequence_diagram" + ShapeCycleDiagram = "cycle" ShapeHierarchy = "hierarchy" ) @@ -969,6 +970,7 @@ var Shapes = []string{ ShapeSQLTable, ShapeImage, ShapeSequenceDiagram, + ShapeCycleDiagram, ShapeHierarchy, } @@ -1037,6 +1039,7 @@ var DSL_SHAPE_TO_SHAPE_TYPE = map[string]string{ ShapeSQLTable: shape.TABLE_TYPE, ShapeImage: shape.IMAGE_TYPE, ShapeSequenceDiagram: shape.SQUARE_TYPE, + ShapeCycleDiagram: shape.SQUARE_TYPE, ShapeHierarchy: shape.SQUARE_TYPE, } diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json new file mode 100644 index 0000000000..21c1b7181a --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -0,0 +1,694 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "a", + "type": "rectangle", + "pos": { + "x": -26, + "y": -233 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "b", + "type": "rectangle", + "pos": { + "x": 173, + "y": -33 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "c", + "type": "rectangle", + "pos": { + "x": -26, + "y": 167 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "d", + "type": "rectangle", + "pos": { + "x": -227, + "y": -32 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(a -> b)[0]", + "src": "a", + "srcArrow": "none", + "dst": "b", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": -200 + }, + { + "x": 10.467000007629395, + "y": -199.72500610351562 + }, + { + "x": 20.905000686645508, + "y": -198.9040069580078 + }, + { + "x": 31.285999298095703, + "y": -197.53700256347656 + }, + { + "x": 41.582000732421875, + "y": -195.62899780273438 + }, + { + "x": 51.76300048828125, + "y": -193.18499755859375 + }, + { + "x": 61.803001403808594, + "y": -190.21099853515625 + }, + { + "x": 71.6729965209961, + "y": -186.71600341796875 + }, + { + "x": 81.34700012207031, + "y": -182.70899963378906 + }, + { + "x": 90.7979965209961, + "y": -178.2010040283203 + }, + { + "x": 100, + "y": -173.2050018310547 + }, + { + "x": 108.927001953125, + "y": -167.73399353027344 + }, + { + "x": 117.55699920654297, + "y": -161.80299377441406 + }, + { + "x": 125.86399841308594, + "y": -155.4290008544922 + }, + { + "x": 133.8260040283203, + "y": -148.6280059814453 + }, + { + "x": 141.42100524902344, + "y": -141.42100524902344 + }, + { + "x": 148.6280059814453, + "y": -133.8260040283203 + }, + { + "x": 155.4290008544922, + "y": -125.86399841308594 + }, + { + "x": 161.80299377441406, + "y": -117.55699920654297 + }, + { + "x": 167.73399353027344, + "y": -108.927001953125 + }, + { + "x": 173.2050018310547, + "y": -100 + }, + { + "x": 178.2010040283203, + "y": -90.7979965209961 + }, + { + "x": 182.70899963378906, + "y": -81.34700012207031 + }, + { + "x": 186.71600341796875, + "y": -71.6729965209961 + }, + { + "x": 190.21099853515625, + "y": -61.803001403808594 + }, + { + "x": 193.18499755859375, + "y": -51.76300048828125 + }, + { + "x": 195.62899780273438, + "y": -41.582000732421875 + }, + { + "x": 197.53700256347656, + "y": -31.285999298095703 + }, + { + "x": 198.9040069580078, + "y": -20.905000686645508 + }, + { + "x": 199.72500610351562, + "y": -10.467000007629395 + }, + { + "x": 200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(b -> c)[0]", + "src": "b", + "srcArrow": "none", + "dst": "c", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 200, + "y": 0 + }, + { + "x": 199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": 198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": 197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": 195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": 193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": 190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": 186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": 182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": 178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": 173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": 167.73399353027344, + "y": 108.927001953125 + }, + { + "x": 161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": 155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": 148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": 141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": 133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": 125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": 117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": 108.927001953125, + "y": 167.73399353027344 + }, + { + "x": 100, + "y": 173.2050018310547 + }, + { + "x": 90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": 81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": 71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": 61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": 51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": 41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": 31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": 20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": 10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": 0, + "y": 200 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(c -> d)[0]", + "src": "c", + "srcArrow": "none", + "dst": "d", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": 200 + }, + { + "x": -10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": -20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": -31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": -41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": -51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": -61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": -71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": -81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": -90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": -99.9990005493164, + "y": 173.2050018310547 + }, + { + "x": -108.927001953125, + "y": 167.73399353027344 + }, + { + "x": -117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": -125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": -133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": -141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": -148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": -155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": -161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": -167.73399353027344, + "y": 108.927001953125 + }, + { + "x": -173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": -178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": -182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": -186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": -190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": -193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": -195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": -197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": -198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": -199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": -200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg new file mode 100644 index 0000000000..4b55a025c0 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg @@ -0,0 +1,98 @@ +abcd + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json new file mode 100644 index 0000000000..21c1b7181a --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -0,0 +1,694 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "a", + "type": "rectangle", + "pos": { + "x": -26, + "y": -233 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "b", + "type": "rectangle", + "pos": { + "x": 173, + "y": -33 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "c", + "type": "rectangle", + "pos": { + "x": -26, + "y": 167 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "d", + "type": "rectangle", + "pos": { + "x": -227, + "y": -32 + }, + "width": 54, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(a -> b)[0]", + "src": "a", + "srcArrow": "none", + "dst": "b", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": -200 + }, + { + "x": 10.467000007629395, + "y": -199.72500610351562 + }, + { + "x": 20.905000686645508, + "y": -198.9040069580078 + }, + { + "x": 31.285999298095703, + "y": -197.53700256347656 + }, + { + "x": 41.582000732421875, + "y": -195.62899780273438 + }, + { + "x": 51.76300048828125, + "y": -193.18499755859375 + }, + { + "x": 61.803001403808594, + "y": -190.21099853515625 + }, + { + "x": 71.6729965209961, + "y": -186.71600341796875 + }, + { + "x": 81.34700012207031, + "y": -182.70899963378906 + }, + { + "x": 90.7979965209961, + "y": -178.2010040283203 + }, + { + "x": 100, + "y": -173.2050018310547 + }, + { + "x": 108.927001953125, + "y": -167.73399353027344 + }, + { + "x": 117.55699920654297, + "y": -161.80299377441406 + }, + { + "x": 125.86399841308594, + "y": -155.4290008544922 + }, + { + "x": 133.8260040283203, + "y": -148.6280059814453 + }, + { + "x": 141.42100524902344, + "y": -141.42100524902344 + }, + { + "x": 148.6280059814453, + "y": -133.8260040283203 + }, + { + "x": 155.4290008544922, + "y": -125.86399841308594 + }, + { + "x": 161.80299377441406, + "y": -117.55699920654297 + }, + { + "x": 167.73399353027344, + "y": -108.927001953125 + }, + { + "x": 173.2050018310547, + "y": -100 + }, + { + "x": 178.2010040283203, + "y": -90.7979965209961 + }, + { + "x": 182.70899963378906, + "y": -81.34700012207031 + }, + { + "x": 186.71600341796875, + "y": -71.6729965209961 + }, + { + "x": 190.21099853515625, + "y": -61.803001403808594 + }, + { + "x": 193.18499755859375, + "y": -51.76300048828125 + }, + { + "x": 195.62899780273438, + "y": -41.582000732421875 + }, + { + "x": 197.53700256347656, + "y": -31.285999298095703 + }, + { + "x": 198.9040069580078, + "y": -20.905000686645508 + }, + { + "x": 199.72500610351562, + "y": -10.467000007629395 + }, + { + "x": 200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(b -> c)[0]", + "src": "b", + "srcArrow": "none", + "dst": "c", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 200, + "y": 0 + }, + { + "x": 199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": 198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": 197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": 195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": 193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": 190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": 186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": 182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": 178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": 173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": 167.73399353027344, + "y": 108.927001953125 + }, + { + "x": 161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": 155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": 148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": 141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": 133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": 125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": 117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": 108.927001953125, + "y": 167.73399353027344 + }, + { + "x": 100, + "y": 173.2050018310547 + }, + { + "x": 90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": 81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": 71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": 61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": 51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": 41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": 31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": 20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": 10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": 0, + "y": 200 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(c -> d)[0]", + "src": "c", + "srcArrow": "none", + "dst": "d", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": 200 + }, + { + "x": -10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": -20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": -31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": -41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": -51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": -61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": -71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": -81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": -90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": -99.9990005493164, + "y": 173.2050018310547 + }, + { + "x": -108.927001953125, + "y": 167.73399353027344 + }, + { + "x": -117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": -125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": -133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": -141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": -148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": -155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": -161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": -167.73399353027344, + "y": 108.927001953125 + }, + { + "x": -173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": -178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": -182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": -186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": -190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": -193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": -195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": -197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": -198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": -199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": -200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg new file mode 100644 index 0000000000..4b55a025c0 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg @@ -0,0 +1,98 @@ +abcd + + + + + + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index fcc6a76192..8478a45883 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -775,3 +775,7 @@ a -> b: hello { b -> c: { icon: https://icons.terrastruct.com/essentials%2F213-alarm.svg } + +-- cycle-diagram -- +shape: cycle +a -> b -> c -> d diff --git a/lib/geo/point.go b/lib/geo/point.go index ab8e034a02..4fb1082396 100644 --- a/lib/geo/point.go +++ b/lib/geo/point.go @@ -324,3 +324,12 @@ func RemovePoints(points Points, toRemove []bool) Points { } return without } + +func (v Vector) Normalize() Vector { + length := v.Length() + if length == 0 { + // avoid dividing by 0 + return Vector{0, 0} + } + return Vector{v[0] / length, v[1] / length} +} From 89bd2e86f57f2bd1e6ac003ca23e0da06202e81d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 19 Feb 2025 09:28:33 -0700 Subject: [PATCH 2/8] test --- .../txtar/cycle-diagram/dagre/board.exp.json | 1263 ++++++++++++++--- .../txtar/cycle-diagram/dagre/sketch.exp.svg | 159 ++- .../txtar/cycle-diagram/elk/board.exp.json | 1235 +++++++++++++--- .../txtar/cycle-diagram/elk/sketch.exp.svg | 167 +-- e2etests/txtar.txt | 14 +- 5 files changed, 2230 insertions(+), 608 deletions(-) diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json index 21c1b7181a..2abd9446ac 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -12,7 +12,48 @@ "fontFamily": "SourceSansPro", "shapes": [ { - "id": "a", + "id": "1", + "type": "cycle", + "pos": { + "x": 0, + "y": 0 + }, + "width": 453, + "height": 466, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 1 + }, + { + "id": "1.a", "type": "rectangle", "pos": { "x": -26, @@ -24,7 +65,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -51,10 +92,10 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "b", + "id": "1.b", "type": "rectangle", "pos": { "x": 173, @@ -66,7 +107,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -93,10 +134,10 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "c", + "id": "1.c", "type": "rectangle", "pos": { "x": -26, @@ -108,7 +149,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -135,10 +176,10 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "d", + "id": "1.d", "type": "rectangle", "pos": { "x": -227, @@ -150,42 +191,802 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 9, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "2", + "type": "cycle", + "pos": { + "x": 513, + "y": 50 + }, + "width": 399, + "height": 366, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 1 + }, + { + "id": "2.a", + "type": "rectangle", + "pos": { + "x": 486, + "y": -183 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "2.b", + "type": "rectangle", + "pos": { + "x": 659, + "y": 116 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "2.c", + "type": "rectangle", + "pos": { + "x": 313, + "y": 117 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "3", + "type": "cycle", + "pos": { + "x": 972, + "y": 0 + }, + "width": 53, + "height": 466, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 1 + }, + { + "id": "3.a", + "type": "rectangle", + "pos": { + "x": 945, + "y": -233 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", "stroke": "B1", "animated": false, - "shadow": false, - "3d": false, - "multiple": false, - "double-border": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "3.b", + "type": "rectangle", + "pos": { + "x": 945, + "y": 167 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "1.(a -> b)[0]", + "src": "1.a", + "srcArrow": "none", + "dst": "1.b", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": -200 + }, + { + "x": 10.467000007629395, + "y": -199.72500610351562 + }, + { + "x": 20.905000686645508, + "y": -198.9040069580078 + }, + { + "x": 31.285999298095703, + "y": -197.53700256347656 + }, + { + "x": 41.582000732421875, + "y": -195.62899780273438 + }, + { + "x": 51.76300048828125, + "y": -193.18499755859375 + }, + { + "x": 61.803001403808594, + "y": -190.21099853515625 + }, + { + "x": 71.6729965209961, + "y": -186.71600341796875 + }, + { + "x": 81.34700012207031, + "y": -182.70899963378906 + }, + { + "x": 90.7979965209961, + "y": -178.2010040283203 + }, + { + "x": 100, + "y": -173.2050018310547 + }, + { + "x": 108.927001953125, + "y": -167.73399353027344 + }, + { + "x": 117.55699920654297, + "y": -161.80299377441406 + }, + { + "x": 125.86399841308594, + "y": -155.4290008544922 + }, + { + "x": 133.8260040283203, + "y": -148.6280059814453 + }, + { + "x": 141.42100524902344, + "y": -141.42100524902344 + }, + { + "x": 148.6280059814453, + "y": -133.8260040283203 + }, + { + "x": 155.4290008544922, + "y": -125.86399841308594 + }, + { + "x": 161.80299377441406, + "y": -117.55699920654297 + }, + { + "x": 167.73399353027344, + "y": -108.927001953125 + }, + { + "x": 173.2050018310547, + "y": -100 + }, + { + "x": 178.2010040283203, + "y": -90.7979965209961 + }, + { + "x": 182.70899963378906, + "y": -81.34700012207031 + }, + { + "x": 186.71600341796875, + "y": -71.6729965209961 + }, + { + "x": 190.21099853515625, + "y": -61.803001403808594 + }, + { + "x": 193.18499755859375, + "y": -51.76300048828125 + }, + { + "x": 195.62899780273438, + "y": -41.582000732421875 + }, + { + "x": 197.53700256347656, + "y": -31.285999298095703 + }, + { + "x": 198.9040069580078, + "y": -20.905000686645508 + }, + { + "x": 199.72500610351562, + "y": -10.467000007629395 + }, + { + "x": 200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "1.(b -> c)[0]", + "src": "1.b", + "srcArrow": "none", + "dst": "1.c", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 200, + "y": 0 + }, + { + "x": 199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": 198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": 197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": 195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": 193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": 190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": 186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": 182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": 178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": 173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": 167.73399353027344, + "y": 108.927001953125 + }, + { + "x": 161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": 155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": 148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": 141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": 133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": 125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": 117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": 108.927001953125, + "y": 167.73399353027344 + }, + { + "x": 100, + "y": 173.2050018310547 + }, + { + "x": 90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": 81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": 71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": 61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": 51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": 41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": 31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": 20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": 10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": 0, + "y": 200 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "1.(c -> d)[0]", + "src": "1.c", + "srcArrow": "none", + "dst": "1.d", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 0, + "y": 200 + }, + { + "x": -10.467000007629395, + "y": 199.72500610351562 + }, + { + "x": -20.905000686645508, + "y": 198.9040069580078 + }, + { + "x": -31.285999298095703, + "y": 197.53700256347656 + }, + { + "x": -41.582000732421875, + "y": 195.62899780273438 + }, + { + "x": -51.76300048828125, + "y": 193.18499755859375 + }, + { + "x": -61.803001403808594, + "y": 190.21099853515625 + }, + { + "x": -71.6729965209961, + "y": 186.71600341796875 + }, + { + "x": -81.34700012207031, + "y": 182.70899963378906 + }, + { + "x": -90.7979965209961, + "y": 178.2010040283203 + }, + { + "x": -99.9990005493164, + "y": 173.2050018310547 + }, + { + "x": -108.927001953125, + "y": 167.73399353027344 + }, + { + "x": -117.55699920654297, + "y": 161.80299377441406 + }, + { + "x": -125.86399841308594, + "y": 155.4290008544922 + }, + { + "x": -133.8260040283203, + "y": 148.6280059814453 + }, + { + "x": -141.42100524902344, + "y": 141.42100524902344 + }, + { + "x": -148.6280059814453, + "y": 133.8260040283203 + }, + { + "x": -155.4290008544922, + "y": 125.86399841308594 + }, + { + "x": -161.80299377441406, + "y": 117.55699920654297 + }, + { + "x": -167.73399353027344, + "y": 108.927001953125 + }, + { + "x": -173.2050018310547, + "y": 99.9990005493164 + }, + { + "x": -178.2010040283203, + "y": 90.7979965209961 + }, + { + "x": -182.70899963378906, + "y": 81.34700012207031 + }, + { + "x": -186.71600341796875, + "y": 71.6729965209961 + }, + { + "x": -190.21099853515625, + "y": 61.803001403808594 + }, + { + "x": -193.18499755859375, + "y": 51.76300048828125 + }, + { + "x": -195.62899780273438, + "y": 41.582000732421875 + }, + { + "x": -197.53700256347656, + "y": 31.285999298095703 + }, + { + "x": -198.9040069580078, + "y": 20.905000686645508 + }, + { + "x": -199.72500610351562, + "y": 10.467000007629395 + }, + { + "x": -200, + "y": 0 + } + ], + "isCurve": true, + "animated": false, "tooltip": "", - "link": "", "icon": null, - "iconPosition": "", - "blend": false, - "fields": null, - "methods": null, - "columns": null, - "label": "d", - "fontSize": 16, - "fontFamily": "DEFAULT", - "language": "", - "color": "N1", - "italic": false, - "bold": true, - "underline": false, - "labelWidth": 9, - "labelHeight": 21, - "labelPosition": "INSIDE_MIDDLE_CENTER", - "zIndex": 0, - "level": 1 - } - ], - "connections": [ + "zIndex": 0 + }, { - "id": "(a -> b)[0]", - "src": "a", + "id": "2.(a -> b)[0]", + "src": "2.a", "srcArrow": "none", - "dst": "b", + "dst": "2.b", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -207,128 +1008,128 @@ "link": "", "route": [ { - "x": 0, - "y": -200 + "x": 513, + "y": -150 }, { - "x": 10.467000007629395, - "y": -199.72500610351562 + "x": 526.9509887695312, + "y": -149.51199340820312 }, { - "x": 20.905000686645508, - "y": -198.9040069580078 + "x": 540.833984375, + "y": -148.05299377441406 }, { - "x": 31.285999298095703, - "y": -197.53700256347656 + "x": 554.5819702148438, + "y": -145.62899780273438 }, { - "x": 41.582000732421875, - "y": -195.62899780273438 + "x": 568.1270141601562, + "y": -142.2519989013672 }, { - "x": 51.76300048828125, - "y": -193.18499755859375 + "x": 581.4039916992188, + "y": -137.93800354003906 }, { - "x": 61.803001403808594, - "y": -190.21099853515625 + "x": 594.3469848632812, + "y": -132.70899963378906 }, { - "x": 71.6729965209961, - "y": -186.71600341796875 + "x": 606.8939819335938, + "y": -126.58899688720703 }, { - "x": 81.34700012207031, - "y": -182.70899963378906 + "x": 618.9829711914062, + "y": -119.60900115966797 }, { - "x": 90.7979965209961, - "y": -178.2010040283203 + "x": 630.5570068359375, + "y": -111.8030014038086 }, { - "x": 100, - "y": -173.2050018310547 + "x": 641.5570068359375, + "y": -103.20800018310547 }, { - "x": 108.927001953125, - "y": -167.73399353027344 + "x": 651.9310302734375, + "y": -93.86699676513672 }, { - "x": 117.55699920654297, - "y": -161.80299377441406 + "x": 661.6279907226562, + "y": -83.82599639892578 }, { - "x": 125.86399841308594, - "y": -155.4290008544922 + "x": 670.6019897460938, + "y": -73.13200378417969 }, { - "x": 133.8260040283203, - "y": -148.6280059814453 + "x": 678.8070068359375, + "y": -61.8380012512207 }, { - "x": 141.42100524902344, - "y": -141.42100524902344 + "x": 686.2050170898438, + "y": -50 }, { - "x": 148.6280059814453, - "y": -133.8260040283203 + "x": 692.7579956054688, + "y": -37.67399978637695 }, { - "x": 155.4290008544922, - "y": -125.86399841308594 + "x": 698.4359741210938, + "y": -24.92099952697754 }, { - "x": 161.80299377441406, - "y": -117.55699920654297 + "x": 703.2109985351562, + "y": -11.803000450134277 }, { - "x": 167.73399353027344, - "y": -108.927001953125 + "x": 707.0590209960938, + "y": 1.6150000095367432 }, { - "x": 173.2050018310547, - "y": -100 + "x": 709.9609985351562, + "y": 15.270000457763672 }, { - "x": 178.2010040283203, - "y": -90.7979965209961 + "x": 711.9039916992188, + "y": 29.0939998626709 }, { - "x": 182.70899963378906, - "y": -81.34700012207031 + "x": 712.8779907226562, + "y": 43.02000045776367 }, { - "x": 186.71600341796875, - "y": -71.6729965209961 + "x": 712.8779907226562, + "y": 56.979000091552734 }, { - "x": 190.21099853515625, - "y": -61.803001403808594 + "x": 711.9039916992188, + "y": 70.90499877929688 }, { - "x": 193.18499755859375, - "y": -51.76300048828125 + "x": 709.9609985351562, + "y": 84.72899627685547 }, { - "x": 195.62899780273438, - "y": -41.582000732421875 + "x": 707.0590209960938, + "y": 98.38400268554688 }, { - "x": 197.53700256347656, - "y": -31.285999298095703 + "x": 703.2109985351562, + "y": 111.8030014038086 }, { - "x": 198.9040069580078, - "y": -20.905000686645508 + "x": 698.4359741210938, + "y": 124.9209976196289 }, { - "x": 199.72500610351562, - "y": -10.467000007629395 + "x": 692.7579956054688, + "y": 137.6739959716797 }, { - "x": 200, - "y": 0 + "x": 686.2050170898438, + "y": 149.99899291992188 } ], "isCurve": true, @@ -338,10 +1139,10 @@ "zIndex": 0 }, { - "id": "(b -> c)[0]", - "src": "b", + "id": "2.(b -> c)[0]", + "src": "2.b", "srcArrow": "none", - "dst": "c", + "dst": "2.c", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -363,128 +1164,128 @@ "link": "", "route": [ { - "x": 200, - "y": 0 + "x": 686.2050170898438, + "y": 149.99899291992188 }, { - "x": 199.72500610351562, - "y": 10.467000007629395 + "x": 678.8070068359375, + "y": 161.83799743652344 }, { - "x": 198.9040069580078, - "y": 20.905000686645508 + "x": 670.6019897460938, + "y": 173.1320037841797 }, { - "x": 197.53700256347656, - "y": 31.285999298095703 + "x": 661.6279907226562, + "y": 183.8260040283203 }, { - "x": 195.62899780273438, - "y": 41.582000732421875 + "x": 651.9310302734375, + "y": 193.86700439453125 }, { - "x": 193.18499755859375, - "y": 51.76300048828125 + "x": 641.5570068359375, + "y": 203.20799255371094 }, { - "x": 190.21099853515625, - "y": 61.803001403808594 + "x": 630.5570068359375, + "y": 211.80299377441406 }, { - "x": 186.71600341796875, - "y": 71.6729965209961 + "x": 618.9829711914062, + "y": 219.60899353027344 }, { - "x": 182.70899963378906, - "y": 81.34700012207031 + "x": 606.8939819335938, + "y": 226.58900451660156 }, { - "x": 178.2010040283203, - "y": 90.7979965209961 + "x": 594.3469848632812, + "y": 232.70899963378906 }, { - "x": 173.2050018310547, - "y": 99.9990005493164 + "x": 581.4039916992188, + "y": 237.93800354003906 }, { - "x": 167.73399353027344, - "y": 108.927001953125 + "x": 568.1270141601562, + "y": 242.2519989013672 }, { - "x": 161.80299377441406, - "y": 117.55699920654297 + "x": 554.5819702148438, + "y": 245.62899780273438 }, { - "x": 155.4290008544922, - "y": 125.86399841308594 + "x": 540.833984375, + "y": 248.05299377441406 }, { - "x": 148.6280059814453, - "y": 133.8260040283203 + "x": 526.9509887695312, + "y": 249.51199340820312 }, { - "x": 141.42100524902344, - "y": 141.42100524902344 + "x": 513, + "y": 250 }, { - "x": 133.8260040283203, - "y": 148.6280059814453 + "x": 499.0480041503906, + "y": 249.51199340820312 }, { - "x": 125.86399841308594, - "y": 155.4290008544922 + "x": 485.1650085449219, + "y": 248.05299377441406 }, { - "x": 117.55699920654297, - "y": 161.80299377441406 + "x": 471.4169921875, + "y": 245.62899780273438 }, { - "x": 108.927001953125, - "y": 167.73399353027344 + "x": 457.87200927734375, + "y": 242.2519989013672 }, { - "x": 100, - "y": 173.2050018310547 + "x": 444.5950012207031, + "y": 237.93800354003906 }, { - "x": 90.7979965209961, - "y": 178.2010040283203 + "x": 431.6520080566406, + "y": 232.70899963378906 }, { - "x": 81.34700012207031, - "y": 182.70899963378906 + "x": 419.1050109863281, + "y": 226.58900451660156 }, { - "x": 71.6729965209961, - "y": 186.71600341796875 + "x": 407.0159912109375, + "y": 219.60899353027344 }, { - "x": 61.803001403808594, - "y": 190.21099853515625 + "x": 395.4419860839844, + "y": 211.80299377441406 }, { - "x": 51.76300048828125, - "y": 193.18499755859375 + "x": 384.4419860839844, + "y": 203.20799255371094 }, { - "x": 41.582000732421875, - "y": 195.62899780273438 + "x": 374.0679931640625, + "y": 193.86700439453125 }, { - "x": 31.285999298095703, - "y": 197.53700256347656 + "x": 364.3710021972656, + "y": 183.8260040283203 }, { - "x": 20.905000686645508, - "y": 198.9040069580078 + "x": 355.3970031738281, + "y": 173.1320037841797 }, { - "x": 10.467000007629395, - "y": 199.72500610351562 + "x": 347.1919860839844, + "y": 161.83799743652344 }, { - "x": 0, - "y": 200 + "x": 339.79400634765625, + "y": 150 } ], "isCurve": true, @@ -494,10 +1295,10 @@ "zIndex": 0 }, { - "id": "(c -> d)[0]", - "src": "c", + "id": "3.(a -> b)[0]", + "src": "3.a", "srcArrow": "none", - "dst": "d", + "dst": "3.b", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -519,128 +1320,128 @@ "link": "", "route": [ { - "x": 0, - "y": 200 + "x": 972, + "y": -200 }, { - "x": -10.467000007629395, - "y": 199.72500610351562 + "x": 992.905029296875, + "y": -198.9040069580078 }, { - "x": -20.905000686645508, - "y": 198.9040069580078 + "x": 1013.5819702148438, + "y": -195.62899780273438 }, { - "x": -31.285999298095703, - "y": 197.53700256347656 + "x": 1033.802978515625, + "y": -190.21099853515625 }, { - "x": -41.582000732421875, - "y": 195.62899780273438 + "x": 1053.3470458984375, + "y": -182.70899963378906 }, { - "x": -51.76300048828125, - "y": 193.18499755859375 + "x": 1072, + "y": -173.2050018310547 }, { - "x": -61.803001403808594, - "y": 190.21099853515625 + "x": 1089.5570068359375, + "y": -161.80299377441406 }, { - "x": -71.6729965209961, - "y": 186.71600341796875 + "x": 1105.8260498046875, + "y": -148.6280059814453 }, { - "x": -81.34700012207031, - "y": 182.70899963378906 + "x": 1120.6280517578125, + "y": -133.8260040283203 }, { - "x": -90.7979965209961, - "y": 178.2010040283203 + "x": 1133.802978515625, + "y": -117.55699920654297 }, { - "x": -99.9990005493164, - "y": 173.2050018310547 + "x": 1145.2049560546875, + "y": -100 }, { - "x": -108.927001953125, - "y": 167.73399353027344 + "x": 1154.708984375, + "y": -81.34700012207031 }, { - "x": -117.55699920654297, - "y": 161.80299377441406 + "x": 1162.2110595703125, + "y": -61.803001403808594 }, { - "x": -125.86399841308594, - "y": 155.4290008544922 + "x": 1167.6290283203125, + "y": -41.582000732421875 }, { - "x": -133.8260040283203, - "y": 148.6280059814453 + "x": 1170.904052734375, + "y": -20.905000686645508 }, { - "x": -141.42100524902344, - "y": 141.42100524902344 + "x": 1172, + "y": 0 }, { - "x": -148.6280059814453, - "y": 133.8260040283203 + "x": 1170.904052734375, + "y": 20.905000686645508 }, { - "x": -155.4290008544922, - "y": 125.86399841308594 + "x": 1167.6290283203125, + "y": 41.582000732421875 }, { - "x": -161.80299377441406, - "y": 117.55699920654297 + "x": 1162.2110595703125, + "y": 61.803001403808594 }, { - "x": -167.73399353027344, - "y": 108.927001953125 + "x": 1154.708984375, + "y": 81.34700012207031 }, { - "x": -173.2050018310547, + "x": 1145.2049560546875, "y": 99.9990005493164 }, { - "x": -178.2010040283203, - "y": 90.7979965209961 + "x": 1133.802978515625, + "y": 117.55699920654297 }, { - "x": -182.70899963378906, - "y": 81.34700012207031 + "x": 1120.6280517578125, + "y": 133.8260040283203 }, { - "x": -186.71600341796875, - "y": 71.6729965209961 + "x": 1105.8260498046875, + "y": 148.6280059814453 }, { - "x": -190.21099853515625, - "y": 61.803001403808594 + "x": 1089.5570068359375, + "y": 161.80299377441406 }, { - "x": -193.18499755859375, - "y": 51.76300048828125 + "x": 1072, + "y": 173.2050018310547 }, { - "x": -195.62899780273438, - "y": 41.582000732421875 + "x": 1053.3470458984375, + "y": 182.70899963378906 }, { - "x": -197.53700256347656, - "y": 31.285999298095703 + "x": 1033.802978515625, + "y": 190.21099853515625 }, { - "x": -198.9040069580078, - "y": 20.905000686645508 + "x": 1013.5819702148438, + "y": 195.62899780273438 }, { - "x": -199.72500610351562, - "y": 10.467000007629395 + "x": 992.905029296875, + "y": 198.9040069580078 }, { - "x": -200, - "y": 0 + "x": 972, + "y": 200 } ], "isCurve": true, diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg index 4b55a025c0..19f6ccf3d4 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ -abcd - + .d2-111884601 .fill-N1{fill:#0A0F25;} + .d2-111884601 .fill-N2{fill:#676C7E;} + .d2-111884601 .fill-N3{fill:#9499AB;} + .d2-111884601 .fill-N4{fill:#CFD2DD;} + .d2-111884601 .fill-N5{fill:#DEE1EB;} + .d2-111884601 .fill-N6{fill:#EEF1F8;} + .d2-111884601 .fill-N7{fill:#FFFFFF;} + .d2-111884601 .fill-B1{fill:#0D32B2;} + .d2-111884601 .fill-B2{fill:#0D32B2;} + .d2-111884601 .fill-B3{fill:#E3E9FD;} + .d2-111884601 .fill-B4{fill:#E3E9FD;} + .d2-111884601 .fill-B5{fill:#EDF0FD;} + .d2-111884601 .fill-B6{fill:#F7F8FE;} + .d2-111884601 .fill-AA2{fill:#4A6FF3;} + .d2-111884601 .fill-AA4{fill:#EDF0FD;} + .d2-111884601 .fill-AA5{fill:#F7F8FE;} + .d2-111884601 .fill-AB4{fill:#EDF0FD;} + .d2-111884601 .fill-AB5{fill:#F7F8FE;} + .d2-111884601 .stroke-N1{stroke:#0A0F25;} + .d2-111884601 .stroke-N2{stroke:#676C7E;} + .d2-111884601 .stroke-N3{stroke:#9499AB;} + .d2-111884601 .stroke-N4{stroke:#CFD2DD;} + .d2-111884601 .stroke-N5{stroke:#DEE1EB;} + .d2-111884601 .stroke-N6{stroke:#EEF1F8;} + .d2-111884601 .stroke-N7{stroke:#FFFFFF;} + .d2-111884601 .stroke-B1{stroke:#0D32B2;} + .d2-111884601 .stroke-B2{stroke:#0D32B2;} + .d2-111884601 .stroke-B3{stroke:#E3E9FD;} + .d2-111884601 .stroke-B4{stroke:#E3E9FD;} + .d2-111884601 .stroke-B5{stroke:#EDF0FD;} + .d2-111884601 .stroke-B6{stroke:#F7F8FE;} + .d2-111884601 .stroke-AA2{stroke:#4A6FF3;} + .d2-111884601 .stroke-AA4{stroke:#EDF0FD;} + .d2-111884601 .stroke-AA5{stroke:#F7F8FE;} + .d2-111884601 .stroke-AB4{stroke:#EDF0FD;} + .d2-111884601 .stroke-AB5{stroke:#F7F8FE;} + .d2-111884601 .background-color-N1{background-color:#0A0F25;} + .d2-111884601 .background-color-N2{background-color:#676C7E;} + .d2-111884601 .background-color-N3{background-color:#9499AB;} + .d2-111884601 .background-color-N4{background-color:#CFD2DD;} + .d2-111884601 .background-color-N5{background-color:#DEE1EB;} + .d2-111884601 .background-color-N6{background-color:#EEF1F8;} + .d2-111884601 .background-color-N7{background-color:#FFFFFF;} + .d2-111884601 .background-color-B1{background-color:#0D32B2;} + .d2-111884601 .background-color-B2{background-color:#0D32B2;} + .d2-111884601 .background-color-B3{background-color:#E3E9FD;} + .d2-111884601 .background-color-B4{background-color:#E3E9FD;} + .d2-111884601 .background-color-B5{background-color:#EDF0FD;} + .d2-111884601 .background-color-B6{background-color:#F7F8FE;} + .d2-111884601 .background-color-AA2{background-color:#4A6FF3;} + .d2-111884601 .background-color-AA4{background-color:#EDF0FD;} + .d2-111884601 .background-color-AA5{background-color:#F7F8FE;} + .d2-111884601 .background-color-AB4{background-color:#EDF0FD;} + .d2-111884601 .background-color-AB5{background-color:#F7F8FE;} + .d2-111884601 .color-N1{color:#0A0F25;} + .d2-111884601 .color-N2{color:#676C7E;} + .d2-111884601 .color-N3{color:#9499AB;} + .d2-111884601 .color-N4{color:#CFD2DD;} + .d2-111884601 .color-N5{color:#DEE1EB;} + .d2-111884601 .color-N6{color:#EEF1F8;} + .d2-111884601 .color-N7{color:#FFFFFF;} + .d2-111884601 .color-B1{color:#0D32B2;} + .d2-111884601 .color-B2{color:#0D32B2;} + .d2-111884601 .color-B3{color:#E3E9FD;} + .d2-111884601 .color-B4{color:#E3E9FD;} + .d2-111884601 .color-B5{color:#EDF0FD;} + .d2-111884601 .color-B6{color:#F7F8FE;} + .d2-111884601 .color-AA2{color:#4A6FF3;} + .d2-111884601 .color-AA4{color:#EDF0FD;} + .d2-111884601 .color-AA5{color:#F7F8FE;} + .d2-111884601 .color-AB4{color:#EDF0FD;} + .d2-111884601 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-111884601);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-111884601);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-111884601);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-111884601);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-111884601);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-111884601);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-111884601);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-111884601);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>abcdabcab + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json index 21c1b7181a..145a26b3b4 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -12,11 +12,52 @@ "fontFamily": "SourceSansPro", "shapes": [ { - "id": "a", + "id": "1", + "type": "cycle", + "pos": { + "x": 12, + "y": 12 + }, + "width": 454, + "height": 466, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 1 + }, + { + "id": "1.a", "type": "rectangle", "pos": { - "x": -26, - "y": -233 + "x": -14, + "y": -221 }, "width": 53, "height": 66, @@ -24,7 +65,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -51,14 +92,14 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "b", + "id": "1.b", "type": "rectangle", "pos": { - "x": 173, - "y": -33 + "x": 185, + "y": -21 }, "width": 53, "height": 66, @@ -66,7 +107,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -93,14 +134,14 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "c", + "id": "1.c", "type": "rectangle", "pos": { - "x": -26, - "y": 167 + "x": -14, + "y": 179 }, "width": 53, "height": 66, @@ -108,7 +149,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -135,14 +176,14 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, - "level": 1 + "level": 2 }, { - "id": "d", + "id": "1.d", "type": "rectangle", "pos": { - "x": -227, - "y": -32 + "x": -215, + "y": -20 }, "width": 54, "height": 66, @@ -150,7 +191,7 @@ "strokeDash": 0, "strokeWidth": 2, "borderRadius": 0, - "fill": "B6", + "fill": "B5", "stroke": "B1", "animated": false, "shadow": false, @@ -177,15 +218,775 @@ "labelHeight": 21, "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, + "level": 2 + }, + { + "id": "2", + "type": "cycle", + "pos": { + "x": 485, + "y": 61 + }, + "width": 400, + "height": 367, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, "level": 1 - } - ], - "connections": [ + }, + { + "id": "2.a", + "type": "rectangle", + "pos": { + "x": 459, + "y": -171 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "2.b", + "type": "rectangle", + "pos": { + "x": 632, + "y": 128 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "2.c", + "type": "rectangle", + "pos": { + "x": 285, + "y": 129 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "3", + "type": "cycle", + "pos": { + "x": 904, + "y": 12 + }, + "width": 53, + "height": 466, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 1 + }, + { + "id": "3.a", + "type": "rectangle", + "pos": { + "x": 878, + "y": -221 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "3.b", + "type": "rectangle", + "pos": { + "x": 878, + "y": 179 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "1.(a -> b)[0]", + "src": "1.a", + "srcArrow": "none", + "dst": "1.b", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 12, + "y": -188 + }, + { + "x": 22.466999053955078, + "y": -187.72500610351562 + }, + { + "x": 32.904998779296875, + "y": -186.9040069580078 + }, + { + "x": 43.2859992980957, + "y": -185.53700256347656 + }, + { + "x": 53.582000732421875, + "y": -183.62899780273438 + }, + { + "x": 63.76300048828125, + "y": -181.18499755859375 + }, + { + "x": 73.8030014038086, + "y": -178.21099853515625 + }, + { + "x": 83.6729965209961, + "y": -174.71600341796875 + }, + { + "x": 93.34700012207031, + "y": -170.70899963378906 + }, + { + "x": 102.7979965209961, + "y": -166.2010040283203 + }, + { + "x": 112, + "y": -161.2050018310547 + }, + { + "x": 120.927001953125, + "y": -155.73399353027344 + }, + { + "x": 129.5570068359375, + "y": -149.80299377441406 + }, + { + "x": 137.86399841308594, + "y": -143.4290008544922 + }, + { + "x": 145.8260040283203, + "y": -136.6280059814453 + }, + { + "x": 153.42100524902344, + "y": -129.42100524902344 + }, + { + "x": 160.6280059814453, + "y": -121.82599639892578 + }, + { + "x": 167.4290008544922, + "y": -113.86399841308594 + }, + { + "x": 173.80299377441406, + "y": -105.55699920654297 + }, + { + "x": 179.73399353027344, + "y": -96.927001953125 + }, + { + "x": 185.2050018310547, + "y": -88 + }, + { + "x": 190.2010040283203, + "y": -78.7979965209961 + }, + { + "x": 194.70899963378906, + "y": -69.34700012207031 + }, + { + "x": 198.71600341796875, + "y": -59.67300033569336 + }, + { + "x": 202.21099853515625, + "y": -49.803001403808594 + }, + { + "x": 205.18499755859375, + "y": -39.76300048828125 + }, + { + "x": 207.62899780273438, + "y": -29.582000732421875 + }, + { + "x": 209.53700256347656, + "y": -19.285999298095703 + }, + { + "x": 210.9040069580078, + "y": -8.904999732971191 + }, + { + "x": 211.72500610351562, + "y": 1.531999945640564 + }, + { + "x": 212, + "y": 12 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "1.(b -> c)[0]", + "src": "1.b", + "srcArrow": "none", + "dst": "1.c", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 212, + "y": 12 + }, + { + "x": 211.72500610351562, + "y": 22.466999053955078 + }, + { + "x": 210.9040069580078, + "y": 32.904998779296875 + }, + { + "x": 209.53700256347656, + "y": 43.2859992980957 + }, + { + "x": 207.62899780273438, + "y": 53.582000732421875 + }, + { + "x": 205.18499755859375, + "y": 63.76300048828125 + }, + { + "x": 202.21099853515625, + "y": 73.8030014038086 + }, + { + "x": 198.71600341796875, + "y": 83.6729965209961 + }, + { + "x": 194.70899963378906, + "y": 93.34700012207031 + }, + { + "x": 190.2010040283203, + "y": 102.7979965209961 + }, + { + "x": 185.2050018310547, + "y": 111.9990005493164 + }, + { + "x": 179.73399353027344, + "y": 120.927001953125 + }, + { + "x": 173.80299377441406, + "y": 129.5570068359375 + }, + { + "x": 167.4290008544922, + "y": 137.86399841308594 + }, + { + "x": 160.6280059814453, + "y": 145.8260040283203 + }, + { + "x": 153.42100524902344, + "y": 153.42100524902344 + }, + { + "x": 145.8260040283203, + "y": 160.6280059814453 + }, + { + "x": 137.86399841308594, + "y": 167.4290008544922 + }, + { + "x": 129.5570068359375, + "y": 173.80299377441406 + }, + { + "x": 120.927001953125, + "y": 179.73399353027344 + }, + { + "x": 112, + "y": 185.2050018310547 + }, + { + "x": 102.7979965209961, + "y": 190.2010040283203 + }, + { + "x": 93.34700012207031, + "y": 194.70899963378906 + }, + { + "x": 83.6729965209961, + "y": 198.71600341796875 + }, + { + "x": 73.8030014038086, + "y": 202.21099853515625 + }, + { + "x": 63.76300048828125, + "y": 205.18499755859375 + }, + { + "x": 53.582000732421875, + "y": 207.62899780273438 + }, + { + "x": 43.2859992980957, + "y": 209.53700256347656 + }, + { + "x": 32.904998779296875, + "y": 210.9040069580078 + }, + { + "x": 22.466999053955078, + "y": 211.72500610351562 + }, + { + "x": 12, + "y": 212 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "1.(c -> d)[0]", + "src": "1.c", + "srcArrow": "none", + "dst": "1.d", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 12, + "y": 212 + }, + { + "x": 1.531999945640564, + "y": 211.72500610351562 + }, + { + "x": -8.904999732971191, + "y": 210.9040069580078 + }, + { + "x": -19.285999298095703, + "y": 209.53700256347656 + }, + { + "x": -29.582000732421875, + "y": 207.62899780273438 + }, + { + "x": -39.76300048828125, + "y": 205.18499755859375 + }, + { + "x": -49.803001403808594, + "y": 202.21099853515625 + }, + { + "x": -59.67300033569336, + "y": 198.71600341796875 + }, + { + "x": -69.34700012207031, + "y": 194.70899963378906 + }, + { + "x": -78.7979965209961, + "y": 190.2010040283203 + }, + { + "x": -87.9990005493164, + "y": 185.2050018310547 + }, + { + "x": -96.927001953125, + "y": 179.73399353027344 + }, + { + "x": -105.55699920654297, + "y": 173.80299377441406 + }, + { + "x": -113.86399841308594, + "y": 167.4290008544922 + }, + { + "x": -121.82599639892578, + "y": 160.6280059814453 + }, + { + "x": -129.42100524902344, + "y": 153.42100524902344 + }, + { + "x": -136.6280059814453, + "y": 145.8260040283203 + }, + { + "x": -143.4290008544922, + "y": 137.86399841308594 + }, + { + "x": -149.80299377441406, + "y": 129.5570068359375 + }, + { + "x": -155.73399353027344, + "y": 120.927001953125 + }, + { + "x": -161.2050018310547, + "y": 111.9990005493164 + }, + { + "x": -166.2010040283203, + "y": 102.7979965209961 + }, + { + "x": -170.70899963378906, + "y": 93.34700012207031 + }, + { + "x": -174.71600341796875, + "y": 83.6729965209961 + }, + { + "x": -178.21099853515625, + "y": 73.8030014038086 + }, + { + "x": -181.18499755859375, + "y": 63.76300048828125 + }, + { + "x": -183.62899780273438, + "y": 53.582000732421875 + }, + { + "x": -185.53700256347656, + "y": 43.2859992980957 + }, + { + "x": -186.9040069580078, + "y": 32.904998779296875 + }, + { + "x": -187.72500610351562, + "y": 22.466999053955078 + }, + { + "x": -188, + "y": 12 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, { - "id": "(a -> b)[0]", - "src": "a", + "id": "2.(a -> b)[0]", + "src": "2.a", "srcArrow": "none", - "dst": "b", + "dst": "2.b", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -207,128 +1008,128 @@ "link": "", "route": [ { - "x": 0, - "y": -200 + "x": 485.5, + "y": -138 }, { - "x": 10.467000007629395, - "y": -199.72500610351562 + "x": 499.45098876953125, + "y": -137.51199340820312 }, { - "x": 20.905000686645508, - "y": -198.9040069580078 + "x": 513.333984375, + "y": -136.05299377441406 }, { - "x": 31.285999298095703, - "y": -197.53700256347656 + "x": 527.0819702148438, + "y": -133.62899780273438 }, { - "x": 41.582000732421875, - "y": -195.62899780273438 + "x": 540.6270141601562, + "y": -130.2519989013672 }, { - "x": 51.76300048828125, - "y": -193.18499755859375 + "x": 553.9039916992188, + "y": -125.93800354003906 }, { - "x": 61.803001403808594, - "y": -190.21099853515625 + "x": 566.8469848632812, + "y": -120.70899963378906 }, { - "x": 71.6729965209961, - "y": -186.71600341796875 + "x": 579.3939819335938, + "y": -114.58899688720703 }, { - "x": 81.34700012207031, - "y": -182.70899963378906 + "x": 591.4829711914062, + "y": -107.60900115966797 }, { - "x": 90.7979965209961, - "y": -178.2010040283203 + "x": 603.0570068359375, + "y": -99.8030014038086 }, { - "x": 100, - "y": -173.2050018310547 + "x": 614.0570068359375, + "y": -91.20800018310547 }, { - "x": 108.927001953125, - "y": -167.73399353027344 + "x": 624.4310302734375, + "y": -81.86699676513672 }, { - "x": 117.55699920654297, - "y": -161.80299377441406 + "x": 634.1279907226562, + "y": -71.82599639892578 }, { - "x": 125.86399841308594, - "y": -155.4290008544922 + "x": 643.1019897460938, + "y": -61.13199996948242 }, { - "x": 133.8260040283203, - "y": -148.6280059814453 + "x": 651.3070068359375, + "y": -49.8380012512207 }, { - "x": 141.42100524902344, - "y": -141.42100524902344 + "x": 658.7050170898438, + "y": -38 }, { - "x": 148.6280059814453, - "y": -133.8260040283203 + "x": 665.2579956054688, + "y": -25.673999786376953 }, { - "x": 155.4290008544922, - "y": -125.86399841308594 + "x": 670.9359741210938, + "y": -12.920999526977539 }, { - "x": 161.80299377441406, - "y": -117.55699920654297 + "x": 675.7109985351562, + "y": 0.19599999487400055 }, { - "x": 167.73399353027344, - "y": -108.927001953125 + "x": 679.5590209960938, + "y": 13.614999771118164 }, { - "x": 173.2050018310547, - "y": -100 + "x": 682.4609985351562, + "y": 27.270000457763672 }, { - "x": 178.2010040283203, - "y": -90.7979965209961 + "x": 684.4039916992188, + "y": 41.09400177001953 }, { - "x": 182.70899963378906, - "y": -81.34700012207031 + "x": 685.3779907226562, + "y": 55.02000045776367 }, { - "x": 186.71600341796875, - "y": -71.6729965209961 + "x": 685.3779907226562, + "y": 68.97899627685547 }, { - "x": 190.21099853515625, - "y": -61.803001403808594 + "x": 684.4039916992188, + "y": 82.90499877929688 }, { - "x": 193.18499755859375, - "y": -51.76300048828125 + "x": 682.4609985351562, + "y": 96.72899627685547 }, { - "x": 195.62899780273438, - "y": -41.582000732421875 + "x": 679.5590209960938, + "y": 110.38400268554688 }, { - "x": 197.53700256347656, - "y": -31.285999298095703 + "x": 675.7109985351562, + "y": 123.8030014038086 }, { - "x": 198.9040069580078, - "y": -20.905000686645508 + "x": 670.9359741210938, + "y": 136.92100524902344 }, { - "x": 199.72500610351562, - "y": -10.467000007629395 + "x": 665.2579956054688, + "y": 149.6739959716797 }, { - "x": 200, - "y": 0 + "x": 658.7050170898438, + "y": 161.99899291992188 } ], "isCurve": true, @@ -338,10 +1139,10 @@ "zIndex": 0 }, { - "id": "(b -> c)[0]", - "src": "b", + "id": "2.(b -> c)[0]", + "src": "2.b", "srcArrow": "none", - "dst": "c", + "dst": "2.c", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -363,128 +1164,128 @@ "link": "", "route": [ { - "x": 200, - "y": 0 + "x": 658.7050170898438, + "y": 161.99899291992188 }, { - "x": 199.72500610351562, - "y": 10.467000007629395 + "x": 651.3070068359375, + "y": 173.83799743652344 }, { - "x": 198.9040069580078, - "y": 20.905000686645508 + "x": 643.1019897460938, + "y": 185.1320037841797 }, { - "x": 197.53700256347656, - "y": 31.285999298095703 + "x": 634.1279907226562, + "y": 195.8260040283203 }, { - "x": 195.62899780273438, - "y": 41.582000732421875 + "x": 624.4310302734375, + "y": 205.86700439453125 }, { - "x": 193.18499755859375, - "y": 51.76300048828125 + "x": 614.0570068359375, + "y": 215.20799255371094 }, { - "x": 190.21099853515625, - "y": 61.803001403808594 + "x": 603.0570068359375, + "y": 223.80299377441406 }, { - "x": 186.71600341796875, - "y": 71.6729965209961 + "x": 591.4829711914062, + "y": 231.60899353027344 }, { - "x": 182.70899963378906, - "y": 81.34700012207031 + "x": 579.3939819335938, + "y": 238.58900451660156 }, { - "x": 178.2010040283203, - "y": 90.7979965209961 + "x": 566.8469848632812, + "y": 244.70899963378906 }, { - "x": 173.2050018310547, - "y": 99.9990005493164 + "x": 553.9039916992188, + "y": 249.93800354003906 }, { - "x": 167.73399353027344, - "y": 108.927001953125 + "x": 540.6270141601562, + "y": 254.2519989013672 }, { - "x": 161.80299377441406, - "y": 117.55699920654297 + "x": 527.0819702148438, + "y": 257.6289978027344 }, { - "x": 155.4290008544922, - "y": 125.86399841308594 + "x": 513.333984375, + "y": 260.0530090332031 }, { - "x": 148.6280059814453, - "y": 133.8260040283203 + "x": 499.45098876953125, + "y": 261.5119934082031 }, { - "x": 141.42100524902344, - "y": 141.42100524902344 + "x": 485.5, + "y": 262 }, { - "x": 133.8260040283203, - "y": 148.6280059814453 + "x": 471.5480041503906, + "y": 261.5119934082031 }, { - "x": 125.86399841308594, - "y": 155.4290008544922 + "x": 457.6650085449219, + "y": 260.0530090332031 }, { - "x": 117.55699920654297, - "y": 161.80299377441406 + "x": 443.9169921875, + "y": 257.6289978027344 }, { - "x": 108.927001953125, - "y": 167.73399353027344 + "x": 430.37200927734375, + "y": 254.2519989013672 }, { - "x": 100, - "y": 173.2050018310547 + "x": 417.0950012207031, + "y": 249.93800354003906 }, { - "x": 90.7979965209961, - "y": 178.2010040283203 + "x": 404.1520080566406, + "y": 244.70899963378906 }, { - "x": 81.34700012207031, - "y": 182.70899963378906 + "x": 391.6050109863281, + "y": 238.58900451660156 }, { - "x": 71.6729965209961, - "y": 186.71600341796875 + "x": 379.5159912109375, + "y": 231.60899353027344 }, { - "x": 61.803001403808594, - "y": 190.21099853515625 + "x": 367.9419860839844, + "y": 223.80299377441406 }, { - "x": 51.76300048828125, - "y": 193.18499755859375 + "x": 356.9419860839844, + "y": 215.20799255371094 }, { - "x": 41.582000732421875, - "y": 195.62899780273438 + "x": 346.5679931640625, + "y": 205.86700439453125 }, { - "x": 31.285999298095703, - "y": 197.53700256347656 + "x": 336.8710021972656, + "y": 195.8260040283203 }, { - "x": 20.905000686645508, - "y": 198.9040069580078 + "x": 327.8970031738281, + "y": 185.1320037841797 }, { - "x": 10.467000007629395, - "y": 199.72500610351562 + "x": 319.6919860839844, + "y": 173.83799743652344 }, { - "x": 0, - "y": 200 + "x": 312.29400634765625, + "y": 162 } ], "isCurve": true, @@ -494,10 +1295,10 @@ "zIndex": 0 }, { - "id": "(c -> d)[0]", - "src": "c", + "id": "3.(a -> b)[0]", + "src": "3.a", "srcArrow": "none", - "dst": "d", + "dst": "3.b", "dstArrow": "triangle", "opacity": 1, "strokeDash": 0, @@ -519,128 +1320,128 @@ "link": "", "route": [ { - "x": 0, - "y": 200 + "x": 904.9099731445312, + "y": -188 }, { - "x": -10.467000007629395, - "y": 199.72500610351562 + "x": 925.8150024414062, + "y": -186.9040069580078 }, { - "x": -20.905000686645508, - "y": 198.9040069580078 + "x": 946.4920043945312, + "y": -183.62899780273438 }, { - "x": -31.285999298095703, - "y": 197.53700256347656 + "x": 966.7130126953125, + "y": -178.21099853515625 }, { - "x": -41.582000732421875, - "y": 195.62899780273438 + "x": 986.2570190429688, + "y": -170.70899963378906 }, { - "x": -51.76300048828125, - "y": 193.18499755859375 + "x": 1004.9099731445312, + "y": -161.2050018310547 }, { - "x": -61.803001403808594, - "y": 190.21099853515625 + "x": 1022.4669799804688, + "y": -149.80299377441406 }, { - "x": -71.6729965209961, - "y": 186.71600341796875 + "x": 1038.7359619140625, + "y": -136.6280059814453 }, { - "x": -81.34700012207031, - "y": 182.70899963378906 + "x": 1053.5389404296875, + "y": -121.82599639892578 }, { - "x": -90.7979965209961, - "y": 178.2010040283203 + "x": 1066.7130126953125, + "y": -105.55699920654297 }, { - "x": -99.9990005493164, - "y": 173.2050018310547 + "x": 1078.114990234375, + "y": -88 }, { - "x": -108.927001953125, - "y": 167.73399353027344 + "x": 1087.6190185546875, + "y": -69.34700012207031 }, { - "x": -117.55699920654297, - "y": 161.80299377441406 + "x": 1095.1209716796875, + "y": -49.803001403808594 }, { - "x": -125.86399841308594, - "y": 155.4290008544922 + "x": 1100.5389404296875, + "y": -29.582000732421875 }, { - "x": -133.8260040283203, - "y": 148.6280059814453 + "x": 1103.81396484375, + "y": -8.904999732971191 }, { - "x": -141.42100524902344, - "y": 141.42100524902344 + "x": 1104.9100341796875, + "y": 12 }, { - "x": -148.6280059814453, - "y": 133.8260040283203 + "x": 1103.81396484375, + "y": 32.904998779296875 }, { - "x": -155.4290008544922, - "y": 125.86399841308594 + "x": 1100.5389404296875, + "y": 53.582000732421875 }, { - "x": -161.80299377441406, - "y": 117.55699920654297 + "x": 1095.1209716796875, + "y": 73.8030014038086 }, { - "x": -167.73399353027344, - "y": 108.927001953125 + "x": 1087.6190185546875, + "y": 93.34700012207031 }, { - "x": -173.2050018310547, - "y": 99.9990005493164 + "x": 1078.114990234375, + "y": 111.9990005493164 }, { - "x": -178.2010040283203, - "y": 90.7979965209961 + "x": 1066.7130126953125, + "y": 129.5570068359375 }, { - "x": -182.70899963378906, - "y": 81.34700012207031 + "x": 1053.5389404296875, + "y": 145.8260040283203 }, { - "x": -186.71600341796875, - "y": 71.6729965209961 + "x": 1038.7359619140625, + "y": 160.6280059814453 }, { - "x": -190.21099853515625, - "y": 61.803001403808594 + "x": 1022.4669799804688, + "y": 173.80299377441406 }, { - "x": -193.18499755859375, - "y": 51.76300048828125 + "x": 1004.9099731445312, + "y": 185.2050018310547 }, { - "x": -195.62899780273438, - "y": 41.582000732421875 + "x": 986.2570190429688, + "y": 194.70899963378906 }, { - "x": -197.53700256347656, - "y": 31.285999298095703 + "x": 966.7130126953125, + "y": 202.21099853515625 }, { - "x": -198.9040069580078, - "y": 20.905000686645508 + "x": 946.4920043945312, + "y": 207.62899780273438 }, { - "x": -199.72500610351562, - "y": 10.467000007629395 + "x": 925.8150024414062, + "y": 210.9040069580078 }, { - "x": -200, - "y": 0 + "x": 904.9099731445312, + "y": 212 } ], "isCurve": true, diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg index 4b55a025c0..19ee385912 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg @@ -1,9 +1,9 @@ -abcd - - - - - + .d2-2474848507 .fill-N1{fill:#0A0F25;} + .d2-2474848507 .fill-N2{fill:#676C7E;} + .d2-2474848507 .fill-N3{fill:#9499AB;} + .d2-2474848507 .fill-N4{fill:#CFD2DD;} + .d2-2474848507 .fill-N5{fill:#DEE1EB;} + .d2-2474848507 .fill-N6{fill:#EEF1F8;} + .d2-2474848507 .fill-N7{fill:#FFFFFF;} + .d2-2474848507 .fill-B1{fill:#0D32B2;} + .d2-2474848507 .fill-B2{fill:#0D32B2;} + .d2-2474848507 .fill-B3{fill:#E3E9FD;} + .d2-2474848507 .fill-B4{fill:#E3E9FD;} + .d2-2474848507 .fill-B5{fill:#EDF0FD;} + .d2-2474848507 .fill-B6{fill:#F7F8FE;} + .d2-2474848507 .fill-AA2{fill:#4A6FF3;} + .d2-2474848507 .fill-AA4{fill:#EDF0FD;} + .d2-2474848507 .fill-AA5{fill:#F7F8FE;} + .d2-2474848507 .fill-AB4{fill:#EDF0FD;} + .d2-2474848507 .fill-AB5{fill:#F7F8FE;} + .d2-2474848507 .stroke-N1{stroke:#0A0F25;} + .d2-2474848507 .stroke-N2{stroke:#676C7E;} + .d2-2474848507 .stroke-N3{stroke:#9499AB;} + .d2-2474848507 .stroke-N4{stroke:#CFD2DD;} + .d2-2474848507 .stroke-N5{stroke:#DEE1EB;} + .d2-2474848507 .stroke-N6{stroke:#EEF1F8;} + .d2-2474848507 .stroke-N7{stroke:#FFFFFF;} + .d2-2474848507 .stroke-B1{stroke:#0D32B2;} + .d2-2474848507 .stroke-B2{stroke:#0D32B2;} + .d2-2474848507 .stroke-B3{stroke:#E3E9FD;} + .d2-2474848507 .stroke-B4{stroke:#E3E9FD;} + .d2-2474848507 .stroke-B5{stroke:#EDF0FD;} + .d2-2474848507 .stroke-B6{stroke:#F7F8FE;} + .d2-2474848507 .stroke-AA2{stroke:#4A6FF3;} + .d2-2474848507 .stroke-AA4{stroke:#EDF0FD;} + .d2-2474848507 .stroke-AA5{stroke:#F7F8FE;} + .d2-2474848507 .stroke-AB4{stroke:#EDF0FD;} + .d2-2474848507 .stroke-AB5{stroke:#F7F8FE;} + .d2-2474848507 .background-color-N1{background-color:#0A0F25;} + .d2-2474848507 .background-color-N2{background-color:#676C7E;} + .d2-2474848507 .background-color-N3{background-color:#9499AB;} + .d2-2474848507 .background-color-N4{background-color:#CFD2DD;} + .d2-2474848507 .background-color-N5{background-color:#DEE1EB;} + .d2-2474848507 .background-color-N6{background-color:#EEF1F8;} + .d2-2474848507 .background-color-N7{background-color:#FFFFFF;} + .d2-2474848507 .background-color-B1{background-color:#0D32B2;} + .d2-2474848507 .background-color-B2{background-color:#0D32B2;} + .d2-2474848507 .background-color-B3{background-color:#E3E9FD;} + .d2-2474848507 .background-color-B4{background-color:#E3E9FD;} + .d2-2474848507 .background-color-B5{background-color:#EDF0FD;} + .d2-2474848507 .background-color-B6{background-color:#F7F8FE;} + .d2-2474848507 .background-color-AA2{background-color:#4A6FF3;} + .d2-2474848507 .background-color-AA4{background-color:#EDF0FD;} + .d2-2474848507 .background-color-AA5{background-color:#F7F8FE;} + .d2-2474848507 .background-color-AB4{background-color:#EDF0FD;} + .d2-2474848507 .background-color-AB5{background-color:#F7F8FE;} + .d2-2474848507 .color-N1{color:#0A0F25;} + .d2-2474848507 .color-N2{color:#676C7E;} + .d2-2474848507 .color-N3{color:#9499AB;} + .d2-2474848507 .color-N4{color:#CFD2DD;} + .d2-2474848507 .color-N5{color:#DEE1EB;} + .d2-2474848507 .color-N6{color:#EEF1F8;} + .d2-2474848507 .color-N7{color:#FFFFFF;} + .d2-2474848507 .color-B1{color:#0D32B2;} + .d2-2474848507 .color-B2{color:#0D32B2;} + .d2-2474848507 .color-B3{color:#E3E9FD;} + .d2-2474848507 .color-B4{color:#E3E9FD;} + .d2-2474848507 .color-B5{color:#EDF0FD;} + .d2-2474848507 .color-B6{color:#F7F8FE;} + .d2-2474848507 .color-AA2{color:#4A6FF3;} + .d2-2474848507 .color-AA4{color:#EDF0FD;} + .d2-2474848507 .color-AA5{color:#F7F8FE;} + .d2-2474848507 .color-AB4{color:#EDF0FD;} + .d2-2474848507 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-2474848507);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-2474848507);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-2474848507);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-2474848507);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-2474848507);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-2474848507);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-2474848507);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-2474848507);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>abcdabcab + + + + + + + + + + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index 8478a45883..b1c425fc29 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -777,5 +777,15 @@ b -> c: { } -- cycle-diagram -- -shape: cycle -a -> b -> c -> d +1: "" { + shape: cycle + a -> b -> c -> d +} +2: "" { + shape: cycle + a -> b -> c +} +3: "" { + shape: cycle + a -> b +} From 0a92e9998e011d5c4f6edfd0de0f71d3c53c0137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yasunori=20Morishima=EF=BC=88=E7=9B=9B=E5=B3=B6=E5=BA=B7?= =?UTF-8?q?=E5=BE=B3=EF=BC=89?= Date: Sat, 2 May 2026 05:23:33 +0000 Subject: [PATCH 3/8] d2cycle: trace arcs to shape borders via analytic circle-rect intersection Closes terrastruct/d2#1578. The previous cycle layout sampled an arc between shape centers and relied on TraceToShape to clip the route. With small shapes sitting on a large radius, every arc sample near the source center fell inside the rectangle, so the segment-based clipper never found an edge crossing and left the curve starting at the shape center. Now the layout solves the layout circle against each rectangle's four edges analytically and limits the arc to the angles where it crosses the source and destination borders, so every connection starts and ends exactly on a shape boundary. Also adds Segment.IntersectCircle to lib/geo and removes the unused clamp helpers from d2cycle/layout.go. --- d2layouts/d2cycle/layout.go | 199 +++-- .../txtar/cycle-diagram/dagre/board.exp.json | 736 +++++++++--------- .../txtar/cycle-diagram/dagre/sketch.exp.svg | 152 ++-- .../txtar/cycle-diagram/elk/board.exp.json | 736 +++++++++--------- .../txtar/cycle-diagram/elk/sketch.exp.svg | 152 ++-- lib/geo/segment.go | 33 + 6 files changed, 1012 insertions(+), 996 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index d957c68142..ad7226105c 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -11,33 +11,28 @@ import ( ) const ( - MIN_RADIUS = 200 - PADDING = 20 - MIN_SEGMENT_LEN = 10 - ARC_STEPS = 30 // high resolution for smooth arcs - + MIN_RADIUS = 200 + PADDING = 20 + ARC_STEPS = 30 ) -// Layout arranges nodes in a circle, ensures label/icon positions are set, -// then routes edges with arcs that get clipped at node borders. +// Layout arranges nodes in a circle and routes each edge as a circular arc +// that starts and ends on the borders of its source and destination shapes. func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error { objects := g.Root.ChildrenArray if len(objects) == 0 { return nil } - // Make sure every object that has label/icon also has a default position for _, obj := range g.Objects { positionLabelsIcons(obj) } - // Arrange objects in a circle radius := calculateRadius(objects) positionObjects(objects, radius) - // Create arcs for _, edge := range g.Edges { - createCircularArc(edge) + createCircularArc(edge, radius) } return nil @@ -50,23 +45,19 @@ func calculateRadius(objects []*d2graph.Object) float64 { size := math.Max(obj.Box.Width, obj.Box.Height) maxSize = math.Max(maxSize, size) } - // ensure enough radius to fit all objects minRadius := (maxSize/2.0 + PADDING) / math.Sin(math.Pi/numObjects) return math.Max(minRadius, MIN_RADIUS) } func positionObjects(objects []*d2graph.Object, radius float64) { numObjects := float64(len(objects)) - // Offset so i=0 is top-center angleOffset := -math.Pi / 2 for i, obj := range objects { angle := angleOffset + (2 * math.Pi * float64(i) / numObjects) - x := radius * math.Cos(angle) y := radius * math.Sin(angle) - // center the box at (x, y) obj.TopLeft = geo.NewPoint( x-obj.Box.Width/2, y-obj.Box.Height/2, @@ -74,134 +65,128 @@ func positionObjects(objects []*d2graph.Object, radius float64) { } } -// createCircularArc samples a smooth arc from center to center, then -// forces the endpoints onto each shape's border, and finally calls -// TraceToShape to clip any additional overrun. -func createCircularArc(edge *d2graph.Edge) { +// createCircularArc routes a single edge as a circular arc whose endpoints +// lie exactly on the borders of the source and destination shapes. The arc +// belongs to the layout circle, centered at the origin with the given radius; +// the source and destination shape centers both lie on that circle. +func createCircularArc(edge *d2graph.Edge, radius float64) { if edge.Src == nil || edge.Dst == nil { return } srcCenter := edge.Src.Center() dstCenter := edge.Dst.Center() + origin := geo.NewPoint(0, 0) - // angles from origin srcAngle := math.Atan2(srcCenter.Y, srcCenter.X) dstAngle := math.Atan2(dstCenter.Y, dstCenter.X) if dstAngle < srcAngle { dstAngle += 2 * math.Pi } + sweep := dstAngle - srcAngle + if sweep <= 0 { + fallbackStraightRoute(edge, srcCenter, dstCenter) + return + } - arcRadius := math.Hypot(srcCenter.X, srcCenter.Y) + startAngle, hasStart := nextBoundaryAngle(edge.Src.Box, origin, radius, srcAngle, sweep, true) + if !hasStart { + startAngle = srcAngle + } + endAngle, hasEnd := nextBoundaryAngle(edge.Dst.Box, origin, radius, srcAngle, sweep, false) + if !hasEnd { + endAngle = dstAngle + } + if endAngle <= startAngle { + fallbackStraightRoute(edge, srcCenter, dstCenter) + return + } - // Sample points along the arc path := make([]*geo.Point, 0, ARC_STEPS+1) for i := 0; i <= ARC_STEPS; i++ { t := float64(i) / float64(ARC_STEPS) - angle := srcAngle + t*(dstAngle-srcAngle) - x := arcRadius * math.Cos(angle) - y := arcRadius * math.Sin(angle) - path = append(path, geo.NewPoint(x, y)) + angle := startAngle + t*(endAngle-startAngle) + path = append(path, geo.NewPoint(radius*math.Cos(angle), radius*math.Sin(angle))) } - // Set start/end to exact centers - path[0] = srcCenter - path[len(path)-1] = dstCenter - // Use TraceToShape to clip route to node borders edge.Route = path - startIndex, endIndex := edge.TraceToShape(edge.Route, 0, len(edge.Route)-1) - if startIndex < endIndex { - edge.Route = edge.Route[startIndex : endIndex+1] - } edge.IsCurve = true } -// clampPointOutsideBox walks forward from 'startIdx' until the path segment -// leaves the bounding box. Then it sets path[startIdx] to the intersection. -// If we never find it, we return (startIdx, path[startIdx]) meaning we can't clamp. -func clampPointOutsideBox(box *geo.Box, path []*geo.Point, startIdx int) (int, *geo.Point) { - if startIdx >= len(path)-1 { - return startIdx, path[startIdx] - } - // If path[startIdx] is outside, no clamp needed - if !boxContains(box, path[startIdx]) { - return startIdx, path[startIdx] - } +func fallbackStraightRoute(edge *d2graph.Edge, srcCenter, dstCenter *geo.Point) { + edge.Route = []*geo.Point{srcCenter, dstCenter} + edge.IsCurve = false +} - // Walk forward looking for outside - for i := startIdx + 1; i < len(path); i++ { - insideNext := boxContains(box, path[i]) - if insideNext { - // still inside -> keep going - continue +// nextBoundaryAngle scans the angles where the layout circle crosses an edge +// of the box. When forSrc is true it returns the smallest such angle strictly +// greater than srcAngle (the point where the arc exits the source box). When +// forSrc is false it returns the largest such angle strictly less than +// srcAngle+sweep (the point where the arc enters the destination box). The +// boolean is false when no crossing exists in the (srcAngle, srcAngle+sweep) +// range, in which case the caller falls back to the shape center. +func nextBoundaryAngle(box *geo.Box, origin *geo.Point, radius, srcAngle, sweep float64, forSrc bool) (float64, bool) { + candidates := boxCircleIntersectionAngles(box, origin, radius) + endAngle := srcAngle + sweep + + var best float64 + found := false + for _, raw := range candidates { + a := raw + for a <= srcAngle { + a += 2 * math.Pi } - // crossing from inside to outside between path[i-1], path[i] - seg := geo.NewSegment(path[i-1], path[i]) - inters := boxIntersections(box, *seg) - if len(inters) > 0 { - // use first intersection - return i, inters[0] + for a > srcAngle+2*math.Pi { + a -= 2 * math.Pi } - // fallback => no intersection found - return i, path[i] - } - // entire remainder is inside, so we can't clamp - // Just return the end - last := len(path) - 1 - return last, path[last] -} - -// clampPointOutsideBoxReverse scans backward from endIdx while path[j] is in the box. -// Once we find crossing (outside→inside), we return (j, intersection). -func clampPointOutsideBoxReverse(box *geo.Box, path []*geo.Point, endIdx int) (int, *geo.Point) { - if endIdx <= 0 { - return endIdx, path[endIdx] - } - if !boxContains(box, path[endIdx]) { - // already outside - return endIdx, path[endIdx] - } - - for j := endIdx - 1; j >= 0; j-- { - if boxContains(box, path[j]) { + if a >= endAngle { + continue + } + if !found { + best = a + found = true continue } - // crossing from outside -> inside between path[j], path[j+1] - seg := geo.NewSegment(path[j], path[j+1]) - inters := boxIntersections(box, *seg) - if len(inters) > 0 { - return j, inters[0] + if forSrc { + if a < best { + best = a + } + } else { + if a > best { + best = a + } } - return j, path[j] } - - // entire path inside - return 0, path[0] + return best, found } -// Helper if your geo.Box doesn’t implement Contains() -func boxContains(b *geo.Box, p *geo.Point) bool { - // typical bounding-box check - return p.X >= b.TopLeft.X && - p.X <= b.TopLeft.X+b.Width && - p.Y >= b.TopLeft.Y && - p.Y <= b.TopLeft.Y+b.Height +func boxCircleIntersectionAngles(box *geo.Box, origin *geo.Point, radius float64) []float64 { + edges := boxEdges(box) + var angles []float64 + for _, e := range edges { + for _, p := range e.IntersectCircle(origin, radius) { + angles = append(angles, math.Atan2(p.Y-origin.Y, p.X-origin.X)) + } + } + return angles } -// Helper if your geo.Box doesn’t implement Intersections(geo.Segment) yet -func boxIntersections(b *geo.Box, seg geo.Segment) []*geo.Point { - // We'll assume d2's standard geo.Box has a built-in Intersections(*Segment) method. - // If not, implement manually. For example, checking each of the 4 edges: - // left, right, top, bottom - // For simplicity, if you do have b.Intersections(...) you can just do: - // return b.Intersections(seg) - return b.Intersections(seg) - // If you don't have that, you'd code the line-rect intersection yourself. +func boxEdges(box *geo.Box) []geo.Segment { + tl := box.TopLeft + tr := geo.NewPoint(tl.X+box.Width, tl.Y) + bl := geo.NewPoint(tl.X, tl.Y+box.Height) + br := geo.NewPoint(tl.X+box.Width, tl.Y+box.Height) + return []geo.Segment{ + {Start: tl, End: tr}, + {Start: tr, End: br}, + {Start: br, End: bl}, + {Start: bl, End: tl}, + } } -// positionLabelsIcons is basically your logic that sets default label/icon positions if needed +// positionLabelsIcons applies a sensible default label/icon position when one +// has not been explicitly specified. func positionLabelsIcons(obj *d2graph.Object) { - // If there's an icon but no icon position, give it a default if obj.Icon != nil && obj.IconPosition == nil { if len(obj.ChildrenArray) > 0 { obj.IconPosition = go2.Pointer(label.OutsideTopLeft.String()) @@ -216,7 +201,6 @@ func positionLabelsIcons(obj *d2graph.Object) { } } - // If there's a label but no label position, give it a default if obj.HasLabel() && obj.LabelPosition == nil { if len(obj.ChildrenArray) > 0 { obj.LabelPosition = go2.Pointer(label.OutsideTopCenter.String()) @@ -228,7 +212,6 @@ func positionLabelsIcons(obj *d2graph.Object) { obj.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String()) } - // If the label is bigger than the shape, fallback to outside positions if float64(obj.LabelDimensions.Width) > obj.Width || float64(obj.LabelDimensions.Height) > obj.Height { if len(obj.ChildrenArray) > 0 { diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json index 2abd9446ac..ab992ed244 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -540,128 +540,128 @@ "link": "", "route": [ { - "x": 0, - "y": -200 + "x": 26.5, + "y": -198.23599243164062 }, { - "x": 10.467000007629395, - "y": -199.72500610351562 + "x": 34.87900161743164, + "y": -196.93499755859375 }, { - "x": 20.905000686645508, - "y": -198.9040069580078 + "x": 43.196998596191406, + "y": -195.2790069580078 }, { - "x": 31.285999298095703, - "y": -197.53700256347656 + "x": 51.43600082397461, + "y": -193.27200317382812 }, { - "x": 41.582000732421875, - "y": -195.62899780273438 + "x": 59.58300018310547, + "y": -190.91799926757812 }, { - "x": 51.76300048828125, - "y": -193.18499755859375 + "x": 67.62300109863281, + "y": -188.22000122070312 }, { - "x": 61.803001403808594, - "y": -190.21099853515625 + "x": 75.54100036621094, + "y": -185.1840057373047 }, { - "x": 71.6729965209961, - "y": -186.71600341796875 + "x": 83.3239974975586, + "y": -181.8159942626953 }, { - "x": 81.34700012207031, - "y": -182.70899963378906 + "x": 90.95600128173828, + "y": -178.1199951171875 }, { - "x": 90.7979965209961, - "y": -178.2010040283203 + "x": 98.4260025024414, + "y": -174.10400390625 }, { - "x": 100, - "y": -173.2050018310547 + "x": 105.71800231933594, + "y": -169.77499389648438 }, { - "x": 108.927001953125, - "y": -167.73399353027344 + "x": 112.81999969482422, + "y": -165.14100646972656 }, { - "x": 117.55699920654297, - "y": -161.80299377441406 + "x": 119.71900177001953, + "y": -160.20899963378906 }, { - "x": 125.86399841308594, - "y": -155.4290008544922 + "x": 126.40299987792969, + "y": -154.99000549316406 }, { - "x": 133.8260040283203, - "y": -148.6280059814453 + "x": 132.86000061035156, + "y": -149.49200439453125 }, { - "x": 141.42100524902344, - "y": -141.42100524902344 + "x": 139.0780029296875, + "y": -143.7259979248047 }, { - "x": 148.6280059814453, - "y": -133.8260040283203 + "x": 145.04600524902344, + "y": -137.7010040283203 }, { - "x": 155.4290008544922, - "y": -125.86399841308594 + "x": 150.7530059814453, + "y": -131.42799377441406 }, { - "x": 161.80299377441406, - "y": -117.55699920654297 + "x": 156.18899536132812, + "y": -124.91899871826172 }, { - "x": 167.73399353027344, - "y": -108.927001953125 + "x": 161.343994140625, + "y": -118.18599700927734 }, { - "x": 173.2050018310547, - "y": -100 + "x": 166.20899963378906, + "y": -111.23999786376953 }, { - "x": 178.2010040283203, - "y": -90.7979965209961 + "x": 170.77499389648438, + "y": -104.09400177001953 }, { - "x": 182.70899963378906, - "y": -81.34700012207031 + "x": 175.03500366210938, + "y": -96.76100158691406 }, { - "x": 186.71600341796875, - "y": -71.6729965209961 + "x": 178.97900390625, + "y": -89.25299835205078 }, { - "x": 190.21099853515625, - "y": -61.803001403808594 + "x": 182.6020050048828, + "y": -81.58599853515625 }, { - "x": 193.18499755859375, - "y": -51.76300048828125 + "x": 185.89599609375, + "y": -73.77200317382812 }, { - "x": 195.62899780273438, - "y": -41.582000732421875 + "x": 188.85699462890625, + "y": -65.82499694824219 }, { - "x": 197.53700256347656, - "y": -31.285999298095703 + "x": 191.4770050048828, + "y": -57.75899887084961 }, { - "x": 198.9040069580078, - "y": -20.905000686645508 + "x": 193.75399780273438, + "y": -49.59000015258789 }, { - "x": 199.72500610351562, - "y": -10.467000007629395 + "x": 195.6820068359375, + "y": -41.332000732421875 }, { - "x": 200, - "y": 0 + "x": 197.25799560546875, + "y": -33 } ], "isCurve": true, @@ -696,128 +696,128 @@ "link": "", "route": [ { - "x": 200, - "y": 0 + "x": 197.25799560546875, + "y": 32.999000549316406 }, { - "x": 199.72500610351562, - "y": 10.467000007629395 + "x": 195.6820068359375, + "y": 41.332000732421875 }, { - "x": 198.9040069580078, - "y": 20.905000686645508 + "x": 193.75399780273438, + "y": 49.59000015258789 }, { - "x": 197.53700256347656, - "y": 31.285999298095703 + "x": 191.4770050048828, + "y": 57.75899887084961 }, { - "x": 195.62899780273438, - "y": 41.582000732421875 + "x": 188.85699462890625, + "y": 65.82499694824219 }, { - "x": 193.18499755859375, - "y": 51.76300048828125 + "x": 185.89599609375, + "y": 73.77200317382812 }, { - "x": 190.21099853515625, - "y": 61.803001403808594 + "x": 182.6020050048828, + "y": 81.58599853515625 }, { - "x": 186.71600341796875, - "y": 71.6729965209961 + "x": 178.97900390625, + "y": 89.25299835205078 }, { - "x": 182.70899963378906, - "y": 81.34700012207031 + "x": 175.03500366210938, + "y": 96.76100158691406 }, { - "x": 178.2010040283203, - "y": 90.7979965209961 + "x": 170.77499389648438, + "y": 104.09400177001953 }, { - "x": 173.2050018310547, - "y": 99.9990005493164 + "x": 166.20899963378906, + "y": 111.23999786376953 }, { - "x": 167.73399353027344, - "y": 108.927001953125 + "x": 161.343994140625, + "y": 118.18599700927734 }, { - "x": 161.80299377441406, - "y": 117.55699920654297 + "x": 156.18899536132812, + "y": 124.91899871826172 }, { - "x": 155.4290008544922, - "y": 125.86399841308594 + "x": 150.7530059814453, + "y": 131.42799377441406 }, { - "x": 148.6280059814453, - "y": 133.8260040283203 + "x": 145.04600524902344, + "y": 137.7010040283203 }, { - "x": 141.42100524902344, - "y": 141.42100524902344 + "x": 139.0780029296875, + "y": 143.7259979248047 }, { - "x": 133.8260040283203, - "y": 148.6280059814453 + "x": 132.86000061035156, + "y": 149.49200439453125 }, { - "x": 125.86399841308594, - "y": 155.4290008544922 + "x": 126.40299987792969, + "y": 154.99000549316406 }, { - "x": 117.55699920654297, - "y": 161.80299377441406 + "x": 119.71900177001953, + "y": 160.20899963378906 }, { - "x": 108.927001953125, - "y": 167.73399353027344 + "x": 112.81999969482422, + "y": 165.14100646972656 }, { - "x": 100, - "y": 173.2050018310547 + "x": 105.71800231933594, + "y": 169.77499389648438 }, { - "x": 90.7979965209961, - "y": 178.2010040283203 + "x": 98.4260025024414, + "y": 174.10400390625 }, { - "x": 81.34700012207031, - "y": 182.70899963378906 + "x": 90.95600128173828, + "y": 178.1199951171875 }, { - "x": 71.6729965209961, - "y": 186.71600341796875 + "x": 83.3239974975586, + "y": 181.8159942626953 }, { - "x": 61.803001403808594, - "y": 190.21099853515625 + "x": 75.54100036621094, + "y": 185.1840057373047 }, { - "x": 51.76300048828125, - "y": 193.18499755859375 + "x": 67.62300109863281, + "y": 188.22000122070312 }, { - "x": 41.582000732421875, - "y": 195.62899780273438 + "x": 59.58300018310547, + "y": 190.91799926757812 }, { - "x": 31.285999298095703, - "y": 197.53700256347656 + "x": 51.43600082397461, + "y": 193.27200317382812 }, { - "x": 20.905000686645508, - "y": 198.9040069580078 + "x": 43.196998596191406, + "y": 195.2790069580078 }, { - "x": 10.467000007629395, - "y": 199.72500610351562 + "x": 34.87900161743164, + "y": 196.93499755859375 }, { - "x": 0, - "y": 200 + "x": 26.5, + "y": 198.23599243164062 } ], "isCurve": true, @@ -852,128 +852,128 @@ "link": "", "route": [ { - "x": 0, - "y": 200 + "x": -26.499000549316406, + "y": 198.23599243164062 }, { - "x": -10.467000007629395, - "y": 199.72500610351562 + "x": -34.87900161743164, + "y": 196.93499755859375 }, { - "x": -20.905000686645508, - "y": 198.9040069580078 + "x": -43.196998596191406, + "y": 195.2790069580078 }, { - "x": -31.285999298095703, - "y": 197.53700256347656 + "x": -51.43600082397461, + "y": 193.27200317382812 }, { - "x": -41.582000732421875, - "y": 195.62899780273438 + "x": -59.58300018310547, + "y": 190.91799926757812 }, { - "x": -51.76300048828125, - "y": 193.18499755859375 + "x": -67.62300109863281, + "y": 188.22000122070312 }, { - "x": -61.803001403808594, - "y": 190.21099853515625 + "x": -75.54100036621094, + "y": 185.1840057373047 }, { - "x": -71.6729965209961, - "y": 186.71600341796875 + "x": -83.3239974975586, + "y": 181.8159942626953 }, { - "x": -81.34700012207031, - "y": 182.70899963378906 + "x": -90.95600128173828, + "y": 178.1199951171875 }, { - "x": -90.7979965209961, - "y": 178.2010040283203 + "x": -98.4260025024414, + "y": 174.10400390625 }, { - "x": -99.9990005493164, - "y": 173.2050018310547 + "x": -105.71800231933594, + "y": 169.77499389648438 }, { - "x": -108.927001953125, - "y": 167.73399353027344 + "x": -112.81999969482422, + "y": 165.14100646972656 }, { - "x": -117.55699920654297, - "y": 161.80299377441406 + "x": -119.71900177001953, + "y": 160.20899963378906 }, { - "x": -125.86399841308594, - "y": 155.4290008544922 + "x": -126.40299987792969, + "y": 154.99000549316406 }, { - "x": -133.8260040283203, - "y": 148.6280059814453 + "x": -132.86000061035156, + "y": 149.49200439453125 }, { - "x": -141.42100524902344, - "y": 141.42100524902344 + "x": -139.0780029296875, + "y": 143.7259979248047 }, { - "x": -148.6280059814453, - "y": 133.8260040283203 + "x": -145.04600524902344, + "y": 137.7010040283203 }, { - "x": -155.4290008544922, - "y": 125.86399841308594 + "x": -150.7530059814453, + "y": 131.42799377441406 }, { - "x": -161.80299377441406, - "y": 117.55699920654297 + "x": -156.18899536132812, + "y": 124.91899871826172 }, { - "x": -167.73399353027344, - "y": 108.927001953125 + "x": -161.343994140625, + "y": 118.18599700927734 }, { - "x": -173.2050018310547, - "y": 99.9990005493164 + "x": -166.20899963378906, + "y": 111.23999786376953 }, { - "x": -178.2010040283203, - "y": 90.7979965209961 + "x": -170.77499389648438, + "y": 104.09400177001953 }, { - "x": -182.70899963378906, - "y": 81.34700012207031 + "x": -175.03500366210938, + "y": 96.76100158691406 }, { - "x": -186.71600341796875, - "y": 71.6729965209961 + "x": -178.97900390625, + "y": 89.25299835205078 }, { - "x": -190.21099853515625, - "y": 61.803001403808594 + "x": -182.6020050048828, + "y": 81.58599853515625 }, { - "x": -193.18499755859375, - "y": 51.76300048828125 + "x": -185.89599609375, + "y": 73.77200317382812 }, { - "x": -195.62899780273438, - "y": 41.582000732421875 + "x": -188.85699462890625, + "y": 65.82499694824219 }, { - "x": -197.53700256347656, - "y": 31.285999298095703 + "x": -191.4770050048828, + "y": 57.75899887084961 }, { - "x": -198.9040069580078, - "y": 20.905000686645508 + "x": -193.75399780273438, + "y": 49.59000015258789 }, { - "x": -199.72500610351562, - "y": 10.467000007629395 + "x": -195.6820068359375, + "y": 41.332000732421875 }, { - "x": -200, - "y": 0 + "x": -197.25799560546875, + "y": 33 } ], "isCurve": true, @@ -1008,128 +1008,128 @@ "link": "", "route": [ { - "x": 513, - "y": -150 + "x": 539.5, + "y": -148.23599243164062 }, { - "x": 526.9509887695312, - "y": -149.51199340820312 + "x": 551.2050170898438, + "y": -146.3159942626953 }, { - "x": 540.833984375, - "y": -148.05299377441406 + "x": 562.7760009765625, + "y": -143.70599365234375 }, { - "x": 554.5819702148438, - "y": -145.62899780273438 + "x": 574.1719970703125, + "y": -140.4149932861328 }, { - "x": 568.1270141601562, - "y": -142.2519989013672 + "x": 585.3519897460938, + "y": -136.4530029296875 }, { - "x": 581.4039916992188, - "y": -137.93800354003906 + "x": 596.2780151367188, + "y": -131.83599853515625 }, { - "x": 594.3469848632812, - "y": -132.70899963378906 + "x": 606.9119873046875, + "y": -126.58000183105469 }, { - "x": 606.8939819335938, - "y": -126.58899688720703 + "x": 617.2150268554688, + "y": -120.7020034790039 }, { - "x": 618.9829711914062, - "y": -119.60900115966797 + "x": 627.1510009765625, + "y": -114.2229995727539 }, { - "x": 630.5570068359375, - "y": -111.8030014038086 + "x": 636.6859741210938, + "y": -107.16699981689453 }, { - "x": 641.5570068359375, - "y": -103.20800018310547 + "x": 645.7849731445312, + "y": -99.55899810791016 }, { - "x": 651.9310302734375, - "y": -93.86699676513672 + "x": 654.4180297851562, + "y": -91.42400360107422 }, { - "x": 661.6279907226562, - "y": -83.82599639892578 + "x": 662.552978515625, + "y": -82.79100036621094 }, { - "x": 670.6019897460938, - "y": -73.13200378417969 + "x": 670.1619873046875, + "y": -73.69200134277344 }, { - "x": 678.8070068359375, - "y": -61.8380012512207 + "x": 677.218994140625, + "y": -64.15699768066406 }, { - "x": 686.2050170898438, - "y": -50 + "x": 683.697021484375, + "y": -54.22100067138672 }, { - "x": 692.7579956054688, - "y": -37.67399978637695 + "x": 689.5759887695312, + "y": -43.91899871826172 }, { - "x": 698.4359741210938, - "y": -24.92099952697754 + "x": 694.8330078125, + "y": -33.2859992980957 }, { - "x": 703.2109985351562, - "y": -11.803000450134277 + "x": 699.4509887695312, + "y": -22.360000610351562 }, { - "x": 707.0590209960938, - "y": 1.6150000095367432 + "x": 703.4119873046875, + "y": -11.178999900817871 }, { - "x": 709.9609985351562, - "y": 15.270000457763672 + "x": 706.7039794921875, + "y": 0.2150000035762787 }, { - "x": 711.9039916992188, - "y": 29.0939998626709 + "x": 709.3150024414062, + "y": 11.78600025177002 }, { - "x": 712.8779907226562, - "y": 43.02000045776367 + "x": 711.2349853515625, + "y": 23.492000579833984 }, { - "x": 712.8779907226562, - "y": 56.979000091552734 + "x": 712.4580078125, + "y": 35.290000915527344 }, { - "x": 711.9039916992188, - "y": 70.90499877929688 + "x": 712.97900390625, + "y": 47.13999938964844 }, { - "x": 709.9609985351562, - "y": 84.72899627685547 + "x": 712.7969970703125, + "y": 59 }, { - "x": 707.0590209960938, - "y": 98.38400268554688 + "x": 711.9119873046875, + "y": 70.8290023803711 }, { - "x": 703.2109985351562, - "y": 111.8030014038086 + "x": 710.3270263671875, + "y": 82.58399963378906 }, { - "x": 698.4359741210938, - "y": 124.9209976196289 + "x": 708.0479736328125, + "y": 94.2249984741211 }, { - "x": 692.7579956054688, - "y": 137.6739959716797 + "x": 705.083984375, + "y": 105.70999908447266 }, { - "x": 686.2050170898438, - "y": 149.99899291992188 + "x": 701.4429931640625, + "y": 116.9990005493164 } ], "isCurve": true, @@ -1164,128 +1164,128 @@ "link": "", "route": [ { - "x": 686.2050170898438, - "y": 149.99899291992188 + "x": 662.3679809570312, + "y": 182.99899291992188 }, { - "x": 678.8070068359375, - "y": 161.83799743652344 + "x": 654.6589965820312, + "y": 191.1820068359375 }, { - "x": 670.6019897460938, - "y": 173.1320037841797 + "x": 646.5020141601562, + "y": 198.91900634765625 }, { - "x": 661.6279907226562, - "y": 183.8260040283203 + "x": 637.9229736328125, + "y": 206.18600463867188 }, { - "x": 651.9310302734375, - "y": 193.86700439453125 + "x": 628.9500122070312, + "y": 212.95799255371094 }, { - "x": 641.5570068359375, - "y": 203.20799255371094 + "x": 619.6099853515625, + "y": 219.21600341796875 }, { - "x": 630.5570068359375, - "y": 211.80299377441406 + "x": 609.9329833984375, + "y": 224.93899536132812 }, { - "x": 618.9829711914062, - "y": 219.60899353027344 + "x": 599.9500122070312, + "y": 230.11000061035156 }, { - "x": 606.8939819335938, - "y": 226.58900451660156 + "x": 589.6920166015625, + "y": 234.71099853515625 }, { - "x": 594.3469848632812, - "y": 232.70899963378906 + "x": 579.1920166015625, + "y": 238.72799682617188 }, { - "x": 581.4039916992188, - "y": 237.93800354003906 + "x": 568.4819946289062, + "y": 242.14999389648438 }, { - "x": 568.1270141601562, - "y": 242.2519989013672 + "x": 557.5980224609375, + "y": 244.96400451660156 }, { - "x": 554.5819702148438, - "y": 245.62899780273438 + "x": 546.572021484375, + "y": 247.16200256347656 }, { - "x": 540.833984375, - "y": 248.05299377441406 + "x": 535.4400024414062, + "y": 248.73699951171875 }, { - "x": 526.9509887695312, - "y": 249.51199340820312 + "x": 524.2379760742188, + "y": 249.6840057373047 }, { "x": 513, "y": 250 }, { - "x": 499.0480041503906, - "y": 249.51199340820312 + "x": 501.760986328125, + "y": 249.6840057373047 }, { - "x": 485.1650085449219, - "y": 248.05299377441406 + "x": 490.5589904785156, + "y": 248.73699951171875 }, { - "x": 471.4169921875, - "y": 245.62899780273438 + "x": 479.427001953125, + "y": 247.16200256347656 }, { - "x": 457.87200927734375, - "y": 242.2519989013672 + "x": 468.4010009765625, + "y": 244.96400451660156 }, { - "x": 444.5950012207031, - "y": 237.93800354003906 + "x": 457.5169982910156, + "y": 242.14999389648438 }, { - "x": 431.6520080566406, - "y": 232.70899963378906 + "x": 446.8070068359375, + "y": 238.72799682617188 }, { - "x": 419.1050109863281, - "y": 226.58900451660156 + "x": 436.3070068359375, + "y": 234.71099853515625 }, { - "x": 407.0159912109375, - "y": 219.60899353027344 + "x": 426.04901123046875, + "y": 230.11000061035156 }, { - "x": 395.4419860839844, - "y": 211.80299377441406 + "x": 416.0660095214844, + "y": 224.93899536132812 }, { - "x": 384.4419860839844, - "y": 203.20799255371094 + "x": 406.3890075683594, + "y": 219.21600341796875 }, { - "x": 374.0679931640625, - "y": 193.86700439453125 + "x": 397.04901123046875, + "y": 212.95799255371094 }, { - "x": 364.3710021972656, - "y": 183.8260040283203 + "x": 388.07598876953125, + "y": 206.18600463867188 }, { - "x": 355.3970031738281, - "y": 173.1320037841797 + "x": 379.49700927734375, + "y": 198.91900634765625 }, { - "x": 347.1919860839844, - "y": 161.83799743652344 + "x": 371.3399963378906, + "y": 191.1820068359375 }, { - "x": 339.79400634765625, - "y": 150 + "x": 363.6310119628906, + "y": 183 } ], "isCurve": true, @@ -1320,128 +1320,128 @@ "link": "", "route": [ { - "x": 972, - "y": -200 + "x": 998.5, + "y": -198.23599243164062 }, { - "x": 992.905029296875, - "y": -198.9040069580078 + "x": 1017.3519897460938, + "y": -194.7899932861328 }, { - "x": 1013.5819702148438, - "y": -195.62899780273438 + "x": 1035.7879638671875, + "y": -189.5540008544922 }, { - "x": 1033.802978515625, - "y": -190.21099853515625 + "x": 1053.637939453125, + "y": -182.57899475097656 }, { - "x": 1053.3470458984375, - "y": -182.70899963378906 + "x": 1070.738037109375, + "y": -173.927001953125 }, { - "x": 1072, - "y": -173.2050018310547 + "x": 1086.9320068359375, + "y": -163.677001953125 }, { - "x": 1089.5570068359375, - "y": -161.80299377441406 + "x": 1102.071044921875, + "y": -151.9250030517578 }, { - "x": 1105.8260498046875, - "y": -148.6280059814453 + "x": 1116.0150146484375, + "y": -138.7779998779297 }, { - "x": 1120.6280517578125, - "y": -133.8260040283203 + "x": 1128.636962890625, + "y": -124.35700225830078 }, { - "x": 1133.802978515625, - "y": -117.55699920654297 + "x": 1139.8199462890625, + "y": -108.79399871826172 }, { - "x": 1145.2049560546875, - "y": -100 + "x": 1149.4630126953125, + "y": -92.23100280761719 }, { - "x": 1154.708984375, - "y": -81.34700012207031 + "x": 1157.4759521484375, + "y": -74.8219985961914 }, { - "x": 1162.2110595703125, - "y": -61.803001403808594 + "x": 1163.7860107421875, + "y": -56.72600173950195 }, { - "x": 1167.6290283203125, - "y": -41.582000732421875 + "x": 1168.3349609375, + "y": -38.10900115966797 }, { - "x": 1170.904052734375, - "y": -20.905000686645508 + "x": 1171.0810546875, + "y": -19.142000198364258 }, { "x": 1172, "y": 0 }, { - "x": 1170.904052734375, - "y": 20.905000686645508 + "x": 1171.0810546875, + "y": 19.142000198364258 }, { - "x": 1167.6290283203125, - "y": 41.582000732421875 + "x": 1168.3349609375, + "y": 38.10900115966797 }, { - "x": 1162.2110595703125, - "y": 61.803001403808594 + "x": 1163.7860107421875, + "y": 56.72600173950195 }, { - "x": 1154.708984375, - "y": 81.34700012207031 + "x": 1157.4759521484375, + "y": 74.8219985961914 }, { - "x": 1145.2049560546875, - "y": 99.9990005493164 + "x": 1149.4630126953125, + "y": 92.23100280761719 }, { - "x": 1133.802978515625, - "y": 117.55699920654297 + "x": 1139.8199462890625, + "y": 108.79399871826172 }, { - "x": 1120.6280517578125, - "y": 133.8260040283203 + "x": 1128.636962890625, + "y": 124.35700225830078 }, { - "x": 1105.8260498046875, - "y": 148.6280059814453 + "x": 1116.0150146484375, + "y": 138.7779998779297 }, { - "x": 1089.5570068359375, - "y": 161.80299377441406 + "x": 1102.071044921875, + "y": 151.9250030517578 }, { - "x": 1072, - "y": 173.2050018310547 + "x": 1086.9320068359375, + "y": 163.677001953125 }, { - "x": 1053.3470458984375, - "y": 182.70899963378906 + "x": 1070.738037109375, + "y": 173.927001953125 }, { - "x": 1033.802978515625, - "y": 190.21099853515625 + "x": 1053.637939453125, + "y": 182.57899475097656 }, { - "x": 1013.5819702148438, - "y": 195.62899780273438 + "x": 1035.7879638671875, + "y": 189.5540008544922 }, { - "x": 992.905029296875, - "y": 198.9040069580078 + "x": 1017.3519897460938, + "y": 194.7899932861328 }, { - "x": 972, - "y": 200 + "x": 998.5, + "y": 198.23599243164062 } ], "isCurve": true, diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg index 19f6ccf3d4..a1bfb352db 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ -abcdabcab + .d2-1185229869 .fill-N1{fill:#0A0F25;} + .d2-1185229869 .fill-N2{fill:#676C7E;} + .d2-1185229869 .fill-N3{fill:#9499AB;} + .d2-1185229869 .fill-N4{fill:#CFD2DD;} + .d2-1185229869 .fill-N5{fill:#DEE1EB;} + .d2-1185229869 .fill-N6{fill:#EEF1F8;} + .d2-1185229869 .fill-N7{fill:#FFFFFF;} + .d2-1185229869 .fill-B1{fill:#0D32B2;} + .d2-1185229869 .fill-B2{fill:#0D32B2;} + .d2-1185229869 .fill-B3{fill:#E3E9FD;} + .d2-1185229869 .fill-B4{fill:#E3E9FD;} + .d2-1185229869 .fill-B5{fill:#EDF0FD;} + .d2-1185229869 .fill-B6{fill:#F7F8FE;} + .d2-1185229869 .fill-AA2{fill:#4A6FF3;} + .d2-1185229869 .fill-AA4{fill:#EDF0FD;} + .d2-1185229869 .fill-AA5{fill:#F7F8FE;} + .d2-1185229869 .fill-AB4{fill:#EDF0FD;} + .d2-1185229869 .fill-AB5{fill:#F7F8FE;} + .d2-1185229869 .stroke-N1{stroke:#0A0F25;} + .d2-1185229869 .stroke-N2{stroke:#676C7E;} + .d2-1185229869 .stroke-N3{stroke:#9499AB;} + .d2-1185229869 .stroke-N4{stroke:#CFD2DD;} + .d2-1185229869 .stroke-N5{stroke:#DEE1EB;} + .d2-1185229869 .stroke-N6{stroke:#EEF1F8;} + .d2-1185229869 .stroke-N7{stroke:#FFFFFF;} + .d2-1185229869 .stroke-B1{stroke:#0D32B2;} + .d2-1185229869 .stroke-B2{stroke:#0D32B2;} + .d2-1185229869 .stroke-B3{stroke:#E3E9FD;} + .d2-1185229869 .stroke-B4{stroke:#E3E9FD;} + .d2-1185229869 .stroke-B5{stroke:#EDF0FD;} + .d2-1185229869 .stroke-B6{stroke:#F7F8FE;} + .d2-1185229869 .stroke-AA2{stroke:#4A6FF3;} + .d2-1185229869 .stroke-AA4{stroke:#EDF0FD;} + .d2-1185229869 .stroke-AA5{stroke:#F7F8FE;} + .d2-1185229869 .stroke-AB4{stroke:#EDF0FD;} + .d2-1185229869 .stroke-AB5{stroke:#F7F8FE;} + .d2-1185229869 .background-color-N1{background-color:#0A0F25;} + .d2-1185229869 .background-color-N2{background-color:#676C7E;} + .d2-1185229869 .background-color-N3{background-color:#9499AB;} + .d2-1185229869 .background-color-N4{background-color:#CFD2DD;} + .d2-1185229869 .background-color-N5{background-color:#DEE1EB;} + .d2-1185229869 .background-color-N6{background-color:#EEF1F8;} + .d2-1185229869 .background-color-N7{background-color:#FFFFFF;} + .d2-1185229869 .background-color-B1{background-color:#0D32B2;} + .d2-1185229869 .background-color-B2{background-color:#0D32B2;} + .d2-1185229869 .background-color-B3{background-color:#E3E9FD;} + .d2-1185229869 .background-color-B4{background-color:#E3E9FD;} + .d2-1185229869 .background-color-B5{background-color:#EDF0FD;} + .d2-1185229869 .background-color-B6{background-color:#F7F8FE;} + .d2-1185229869 .background-color-AA2{background-color:#4A6FF3;} + .d2-1185229869 .background-color-AA4{background-color:#EDF0FD;} + .d2-1185229869 .background-color-AA5{background-color:#F7F8FE;} + .d2-1185229869 .background-color-AB4{background-color:#EDF0FD;} + .d2-1185229869 .background-color-AB5{background-color:#F7F8FE;} + .d2-1185229869 .color-N1{color:#0A0F25;} + .d2-1185229869 .color-N2{color:#676C7E;} + .d2-1185229869 .color-N3{color:#9499AB;} + .d2-1185229869 .color-N4{color:#CFD2DD;} + .d2-1185229869 .color-N5{color:#DEE1EB;} + .d2-1185229869 .color-N6{color:#EEF1F8;} + .d2-1185229869 .color-N7{color:#FFFFFF;} + .d2-1185229869 .color-B1{color:#0D32B2;} + .d2-1185229869 .color-B2{color:#0D32B2;} + .d2-1185229869 .color-B3{color:#E3E9FD;} + .d2-1185229869 .color-B4{color:#E3E9FD;} + .d2-1185229869 .color-B5{color:#EDF0FD;} + .d2-1185229869 .color-B6{color:#F7F8FE;} + .d2-1185229869 .color-AA2{color:#4A6FF3;} + .d2-1185229869 .color-AA4{color:#EDF0FD;} + .d2-1185229869 .color-AA5{color:#F7F8FE;} + .d2-1185229869 .color-AB4{color:#EDF0FD;} + .d2-1185229869 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-1185229869);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-1185229869);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-1185229869);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-1185229869);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-1185229869);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-1185229869);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-1185229869);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-1185229869);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>abcdabcab diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json index 145a26b3b4..f1c964734b 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -540,128 +540,128 @@ "link": "", "route": [ { - "x": 12, - "y": -188 + "x": 38.5, + "y": -186.23599243164062 }, { - "x": 22.466999053955078, - "y": -187.72500610351562 + "x": 46.87900161743164, + "y": -184.93499755859375 }, { - "x": 32.904998779296875, - "y": -186.9040069580078 + "x": 55.196998596191406, + "y": -183.2790069580078 }, { - "x": 43.2859992980957, - "y": -185.53700256347656 + "x": 63.43600082397461, + "y": -181.27200317382812 }, { - "x": 53.582000732421875, - "y": -183.62899780273438 + "x": 71.58300018310547, + "y": -178.91799926757812 }, { - "x": 63.76300048828125, - "y": -181.18499755859375 + "x": 79.62300109863281, + "y": -176.22000122070312 }, { - "x": 73.8030014038086, - "y": -178.21099853515625 + "x": 87.54100036621094, + "y": -173.1840057373047 }, { - "x": 83.6729965209961, - "y": -174.71600341796875 + "x": 95.3239974975586, + "y": -169.8159942626953 }, { - "x": 93.34700012207031, - "y": -170.70899963378906 + "x": 102.95600128173828, + "y": -166.1199951171875 }, { - "x": 102.7979965209961, - "y": -166.2010040283203 + "x": 110.4260025024414, + "y": -162.10400390625 }, { - "x": 112, - "y": -161.2050018310547 + "x": 117.71800231933594, + "y": -157.77499389648438 }, { - "x": 120.927001953125, - "y": -155.73399353027344 + "x": 124.81999969482422, + "y": -153.14100646972656 }, { - "x": 129.5570068359375, - "y": -149.80299377441406 + "x": 131.718994140625, + "y": -148.20899963378906 }, { - "x": 137.86399841308594, - "y": -143.4290008544922 + "x": 138.4029998779297, + "y": -142.99000549316406 }, { - "x": 145.8260040283203, - "y": -136.6280059814453 + "x": 144.86000061035156, + "y": -137.49200439453125 }, { - "x": 153.42100524902344, - "y": -129.42100524902344 + "x": 151.0780029296875, + "y": -131.7259979248047 }, { - "x": 160.6280059814453, - "y": -121.82599639892578 + "x": 157.04600524902344, + "y": -125.70099639892578 }, { - "x": 167.4290008544922, - "y": -113.86399841308594 + "x": 162.7530059814453, + "y": -119.4280014038086 }, { - "x": 173.80299377441406, - "y": -105.55699920654297 + "x": 168.18899536132812, + "y": -112.91899871826172 }, { - "x": 179.73399353027344, - "y": -96.927001953125 + "x": 173.343994140625, + "y": -106.18599700927734 }, { - "x": 185.2050018310547, - "y": -88 + "x": 178.20899963378906, + "y": -99.23999786376953 }, { - "x": 190.2010040283203, - "y": -78.7979965209961 + "x": 182.77499389648438, + "y": -92.09400177001953 }, { - "x": 194.70899963378906, - "y": -69.34700012207031 + "x": 187.03500366210938, + "y": -84.76100158691406 }, { - "x": 198.71600341796875, - "y": -59.67300033569336 + "x": 190.97900390625, + "y": -77.25299835205078 }, { - "x": 202.21099853515625, - "y": -49.803001403808594 + "x": 194.6020050048828, + "y": -69.58599853515625 }, { - "x": 205.18499755859375, - "y": -39.76300048828125 + "x": 197.89599609375, + "y": -61.77199935913086 }, { - "x": 207.62899780273438, - "y": -29.582000732421875 + "x": 200.85699462890625, + "y": -53.82500076293945 }, { - "x": 209.53700256347656, - "y": -19.285999298095703 + "x": 203.4770050048828, + "y": -45.75899887084961 }, { - "x": 210.9040069580078, - "y": -8.904999732971191 + "x": 205.75399780273438, + "y": -37.59000015258789 }, { - "x": 211.72500610351562, - "y": 1.531999945640564 + "x": 207.6820068359375, + "y": -29.332000732421875 }, { - "x": 212, - "y": 12 + "x": 209.25799560546875, + "y": -21 } ], "isCurve": true, @@ -696,128 +696,128 @@ "link": "", "route": [ { - "x": 212, - "y": 12 + "x": 209.25799560546875, + "y": 44.999000549316406 }, { - "x": 211.72500610351562, - "y": 22.466999053955078 + "x": 207.6820068359375, + "y": 53.332000732421875 }, { - "x": 210.9040069580078, - "y": 32.904998779296875 + "x": 205.75399780273438, + "y": 61.59000015258789 }, { - "x": 209.53700256347656, - "y": 43.2859992980957 + "x": 203.4770050048828, + "y": 69.75900268554688 }, { - "x": 207.62899780273438, - "y": 53.582000732421875 + "x": 200.85699462890625, + "y": 77.82499694824219 }, { - "x": 205.18499755859375, - "y": 63.76300048828125 + "x": 197.89599609375, + "y": 85.77200317382812 }, { - "x": 202.21099853515625, - "y": 73.8030014038086 + "x": 194.6020050048828, + "y": 93.58599853515625 }, { - "x": 198.71600341796875, - "y": 83.6729965209961 + "x": 190.97900390625, + "y": 101.25299835205078 }, { - "x": 194.70899963378906, - "y": 93.34700012207031 + "x": 187.03500366210938, + "y": 108.76100158691406 }, { - "x": 190.2010040283203, - "y": 102.7979965209961 + "x": 182.77499389648438, + "y": 116.09400177001953 }, { - "x": 185.2050018310547, - "y": 111.9990005493164 + "x": 178.20899963378906, + "y": 123.23999786376953 }, { - "x": 179.73399353027344, - "y": 120.927001953125 + "x": 173.343994140625, + "y": 130.18600463867188 }, { - "x": 173.80299377441406, - "y": 129.5570068359375 + "x": 168.18899536132812, + "y": 136.91900634765625 }, { - "x": 167.4290008544922, - "y": 137.86399841308594 + "x": 162.7530059814453, + "y": 143.42799377441406 }, { - "x": 160.6280059814453, - "y": 145.8260040283203 + "x": 157.04600524902344, + "y": 149.7010040283203 }, { - "x": 153.42100524902344, - "y": 153.42100524902344 + "x": 151.0780029296875, + "y": 155.7259979248047 }, { - "x": 145.8260040283203, - "y": 160.6280059814453 + "x": 144.86000061035156, + "y": 161.49200439453125 }, { - "x": 137.86399841308594, - "y": 167.4290008544922 + "x": 138.4029998779297, + "y": 166.99000549316406 }, { - "x": 129.5570068359375, - "y": 173.80299377441406 + "x": 131.718994140625, + "y": 172.20899963378906 }, { - "x": 120.927001953125, - "y": 179.73399353027344 + "x": 124.81999969482422, + "y": 177.14100646972656 }, { - "x": 112, - "y": 185.2050018310547 + "x": 117.71800231933594, + "y": 181.77499389648438 }, { - "x": 102.7979965209961, - "y": 190.2010040283203 + "x": 110.4260025024414, + "y": 186.10400390625 }, { - "x": 93.34700012207031, - "y": 194.70899963378906 + "x": 102.95600128173828, + "y": 190.1199951171875 }, { - "x": 83.6729965209961, - "y": 198.71600341796875 + "x": 95.3239974975586, + "y": 193.8159942626953 }, { - "x": 73.8030014038086, - "y": 202.21099853515625 + "x": 87.54100036621094, + "y": 197.1840057373047 }, { - "x": 63.76300048828125, - "y": 205.18499755859375 + "x": 79.62300109863281, + "y": 200.22000122070312 }, { - "x": 53.582000732421875, - "y": 207.62899780273438 + "x": 71.58300018310547, + "y": 202.91799926757812 }, { - "x": 43.2859992980957, - "y": 209.53700256347656 + "x": 63.43600082397461, + "y": 205.27200317382812 }, { - "x": 32.904998779296875, - "y": 210.9040069580078 + "x": 55.196998596191406, + "y": 207.2790069580078 }, { - "x": 22.466999053955078, - "y": 211.72500610351562 + "x": 46.87900161743164, + "y": 208.93499755859375 }, { - "x": 12, - "y": 212 + "x": 38.5, + "y": 210.23599243164062 } ], "isCurve": true, @@ -852,128 +852,128 @@ "link": "", "route": [ { - "x": 12, - "y": 212 + "x": -14.49899959564209, + "y": 210.23599243164062 }, { - "x": 1.531999945640564, - "y": 211.72500610351562 + "x": -22.878999710083008, + "y": 208.93499755859375 }, { - "x": -8.904999732971191, - "y": 210.9040069580078 + "x": -31.19700050354004, + "y": 207.2790069580078 }, { - "x": -19.285999298095703, - "y": 209.53700256347656 + "x": -39.43600082397461, + "y": 205.27200317382812 }, { - "x": -29.582000732421875, - "y": 207.62899780273438 + "x": -47.58300018310547, + "y": 202.91799926757812 }, { - "x": -39.76300048828125, - "y": 205.18499755859375 + "x": -55.62300109863281, + "y": 200.22000122070312 }, { - "x": -49.803001403808594, - "y": 202.21099853515625 + "x": -63.54100036621094, + "y": 197.1840057373047 }, { - "x": -59.67300033569336, - "y": 198.71600341796875 + "x": -71.3239974975586, + "y": 193.8159942626953 }, { - "x": -69.34700012207031, - "y": 194.70899963378906 + "x": -78.95600128173828, + "y": 190.1199951171875 }, { - "x": -78.7979965209961, - "y": 190.2010040283203 + "x": -86.4260025024414, + "y": 186.10400390625 }, { - "x": -87.9990005493164, - "y": 185.2050018310547 + "x": -93.71800231933594, + "y": 181.77499389648438 }, { - "x": -96.927001953125, - "y": 179.73399353027344 + "x": -100.81999969482422, + "y": 177.14100646972656 }, { - "x": -105.55699920654297, - "y": 173.80299377441406 + "x": -107.71900177001953, + "y": 172.20899963378906 }, { - "x": -113.86399841308594, - "y": 167.4290008544922 + "x": -114.40299987792969, + "y": 166.99000549316406 }, { - "x": -121.82599639892578, - "y": 160.6280059814453 + "x": -120.86000061035156, + "y": 161.49200439453125 }, { - "x": -129.42100524902344, - "y": 153.42100524902344 + "x": -127.0780029296875, + "y": 155.7259979248047 }, { - "x": -136.6280059814453, - "y": 145.8260040283203 + "x": -133.04600524902344, + "y": 149.7010040283203 }, { - "x": -143.4290008544922, - "y": 137.86399841308594 + "x": -138.7530059814453, + "y": 143.42799377441406 }, { - "x": -149.80299377441406, - "y": 129.5570068359375 + "x": -144.18899536132812, + "y": 136.91900634765625 }, { - "x": -155.73399353027344, - "y": 120.927001953125 + "x": -149.343994140625, + "y": 130.18600463867188 }, { - "x": -161.2050018310547, - "y": 111.9990005493164 + "x": -154.20899963378906, + "y": 123.23999786376953 }, { - "x": -166.2010040283203, - "y": 102.7979965209961 + "x": -158.77499389648438, + "y": 116.09400177001953 }, { - "x": -170.70899963378906, - "y": 93.34700012207031 + "x": -163.03500366210938, + "y": 108.76100158691406 }, { - "x": -174.71600341796875, - "y": 83.6729965209961 + "x": -166.97900390625, + "y": 101.25299835205078 }, { - "x": -178.21099853515625, - "y": 73.8030014038086 + "x": -170.6020050048828, + "y": 93.58599853515625 }, { - "x": -181.18499755859375, - "y": 63.76300048828125 + "x": -173.89599609375, + "y": 85.77200317382812 }, { - "x": -183.62899780273438, - "y": 53.582000732421875 + "x": -176.85699462890625, + "y": 77.82499694824219 }, { - "x": -185.53700256347656, - "y": 43.2859992980957 + "x": -179.4770050048828, + "y": 69.75900268554688 }, { - "x": -186.9040069580078, - "y": 32.904998779296875 + "x": -181.75399780273438, + "y": 61.59000015258789 }, { - "x": -187.72500610351562, - "y": 22.466999053955078 + "x": -183.6820068359375, + "y": 53.332000732421875 }, { - "x": -188, - "y": 12 + "x": -185.25799560546875, + "y": 45 } ], "isCurve": true, @@ -1008,128 +1008,128 @@ "link": "", "route": [ { - "x": 485.5, - "y": -138 + "x": 512, + "y": -136.23599243164062 }, { - "x": 499.45098876953125, - "y": -137.51199340820312 + "x": 523.7050170898438, + "y": -134.3159942626953 }, { - "x": 513.333984375, - "y": -136.05299377441406 + "x": 535.2760009765625, + "y": -131.70599365234375 }, { - "x": 527.0819702148438, - "y": -133.62899780273438 + "x": 546.6719970703125, + "y": -128.4149932861328 }, { - "x": 540.6270141601562, - "y": -130.2519989013672 + "x": 557.8519897460938, + "y": -124.4530029296875 }, { - "x": 553.9039916992188, - "y": -125.93800354003906 + "x": 568.7780151367188, + "y": -119.83599853515625 }, { - "x": 566.8469848632812, - "y": -120.70899963378906 + "x": 579.4119873046875, + "y": -114.58000183105469 }, { - "x": 579.3939819335938, - "y": -114.58899688720703 + "x": 589.7150268554688, + "y": -108.7020034790039 }, { - "x": 591.4829711914062, - "y": -107.60900115966797 + "x": 599.6510009765625, + "y": -102.2229995727539 }, { - "x": 603.0570068359375, - "y": -99.8030014038086 + "x": 609.1859741210938, + "y": -95.16699981689453 }, { - "x": 614.0570068359375, - "y": -91.20800018310547 + "x": 618.2849731445312, + "y": -87.55899810791016 }, { - "x": 624.4310302734375, - "y": -81.86699676513672 + "x": 626.9180297851562, + "y": -79.42400360107422 }, { - "x": 634.1279907226562, - "y": -71.82599639892578 + "x": 635.052978515625, + "y": -70.79100036621094 }, { - "x": 643.1019897460938, - "y": -61.13199996948242 + "x": 642.6619873046875, + "y": -61.69200134277344 }, { - "x": 651.3070068359375, - "y": -49.8380012512207 + "x": 649.718994140625, + "y": -52.15700149536133 }, { - "x": 658.7050170898438, - "y": -38 + "x": 656.197021484375, + "y": -42.22100067138672 }, { - "x": 665.2579956054688, - "y": -25.673999786376953 + "x": 662.0759887695312, + "y": -31.91900062561035 }, { - "x": 670.9359741210938, - "y": -12.920999526977539 + "x": 667.3330078125, + "y": -21.285999298095703 }, { - "x": 675.7109985351562, - "y": 0.19599999487400055 + "x": 671.9509887695312, + "y": -10.359999656677246 }, { - "x": 679.5590209960938, - "y": 13.614999771118164 + "x": 675.9119873046875, + "y": 0.8199999928474426 }, { - "x": 682.4609985351562, - "y": 27.270000457763672 + "x": 679.2039794921875, + "y": 12.21500015258789 }, { - "x": 684.4039916992188, - "y": 41.09400177001953 + "x": 681.8150024414062, + "y": 23.785999298095703 }, { - "x": 685.3779907226562, - "y": 55.02000045776367 + "x": 683.7349853515625, + "y": 35.492000579833984 }, { - "x": 685.3779907226562, - "y": 68.97899627685547 + "x": 684.9580078125, + "y": 47.290000915527344 }, { - "x": 684.4039916992188, - "y": 82.90499877929688 + "x": 685.47900390625, + "y": 59.13999938964844 }, { - "x": 682.4609985351562, - "y": 96.72899627685547 + "x": 685.2969970703125, + "y": 71 }, { - "x": 679.5590209960938, - "y": 110.38400268554688 + "x": 684.4119873046875, + "y": 82.8290023803711 }, { - "x": 675.7109985351562, - "y": 123.8030014038086 + "x": 682.8270263671875, + "y": 94.58399963378906 }, { - "x": 670.9359741210938, - "y": 136.92100524902344 + "x": 680.5479736328125, + "y": 106.2249984741211 }, { - "x": 665.2579956054688, - "y": 149.6739959716797 + "x": 677.583984375, + "y": 117.70999908447266 }, { - "x": 658.7050170898438, - "y": 161.99899291992188 + "x": 673.9429931640625, + "y": 128.99899291992188 } ], "isCurve": true, @@ -1164,128 +1164,128 @@ "link": "", "route": [ { - "x": 658.7050170898438, - "y": 161.99899291992188 + "x": 634.8679809570312, + "y": 194.99899291992188 }, { - "x": 651.3070068359375, - "y": 173.83799743652344 + "x": 627.1589965820312, + "y": 203.1820068359375 }, { - "x": 643.1019897460938, - "y": 185.1320037841797 + "x": 619.0020141601562, + "y": 210.91900634765625 }, { - "x": 634.1279907226562, - "y": 195.8260040283203 + "x": 610.4229736328125, + "y": 218.18600463867188 }, { - "x": 624.4310302734375, - "y": 205.86700439453125 + "x": 601.4500122070312, + "y": 224.95799255371094 }, { - "x": 614.0570068359375, - "y": 215.20799255371094 + "x": 592.1099853515625, + "y": 231.21600341796875 }, { - "x": 603.0570068359375, - "y": 223.80299377441406 + "x": 582.4329833984375, + "y": 236.93899536132812 }, { - "x": 591.4829711914062, - "y": 231.60899353027344 + "x": 572.4500122070312, + "y": 242.11000061035156 }, { - "x": 579.3939819335938, - "y": 238.58900451660156 + "x": 562.1920166015625, + "y": 246.71099853515625 }, { - "x": 566.8469848632812, - "y": 244.70899963378906 + "x": 551.6920166015625, + "y": 250.72799682617188 }, { - "x": 553.9039916992188, - "y": 249.93800354003906 + "x": 540.9819946289062, + "y": 254.14999389648438 }, { - "x": 540.6270141601562, - "y": 254.2519989013672 + "x": 530.0980224609375, + "y": 256.9639892578125 }, { - "x": 527.0819702148438, - "y": 257.6289978027344 + "x": 519.072021484375, + "y": 259.1619873046875 }, { - "x": 513.333984375, - "y": 260.0530090332031 + "x": 507.94000244140625, + "y": 260.73699951171875 }, { - "x": 499.45098876953125, - "y": 261.5119934082031 + "x": 496.7380065917969, + "y": 261.6839904785156 }, { "x": 485.5, "y": 262 }, { - "x": 471.5480041503906, - "y": 261.5119934082031 + "x": 474.260986328125, + "y": 261.6839904785156 }, { - "x": 457.6650085449219, - "y": 260.0530090332031 + "x": 463.0589904785156, + "y": 260.73699951171875 }, { - "x": 443.9169921875, - "y": 257.6289978027344 + "x": 451.927001953125, + "y": 259.1619873046875 }, { - "x": 430.37200927734375, - "y": 254.2519989013672 + "x": 440.9010009765625, + "y": 256.9639892578125 }, { - "x": 417.0950012207031, - "y": 249.93800354003906 + "x": 430.0169982910156, + "y": 254.14999389648438 }, { - "x": 404.1520080566406, - "y": 244.70899963378906 + "x": 419.3070068359375, + "y": 250.72799682617188 }, { - "x": 391.6050109863281, - "y": 238.58900451660156 + "x": 408.8070068359375, + "y": 246.71099853515625 }, { - "x": 379.5159912109375, - "y": 231.60899353027344 + "x": 398.54901123046875, + "y": 242.11000061035156 }, { - "x": 367.9419860839844, - "y": 223.80299377441406 + "x": 388.5660095214844, + "y": 236.93899536132812 }, { - "x": 356.9419860839844, - "y": 215.20799255371094 + "x": 378.8890075683594, + "y": 231.21600341796875 }, { - "x": 346.5679931640625, - "y": 205.86700439453125 + "x": 369.54901123046875, + "y": 224.95799255371094 }, { - "x": 336.8710021972656, - "y": 195.8260040283203 + "x": 360.57598876953125, + "y": 218.18600463867188 }, { - "x": 327.8970031738281, - "y": 185.1320037841797 + "x": 351.99700927734375, + "y": 210.91900634765625 }, { - "x": 319.6919860839844, - "y": 173.83799743652344 + "x": 343.8399963378906, + "y": 203.1820068359375 }, { - "x": 312.29400634765625, - "y": 162 + "x": 336.1310119628906, + "y": 195 } ], "isCurve": true, @@ -1320,128 +1320,128 @@ "link": "", "route": [ { - "x": 904.9099731445312, - "y": -188 + "x": 931.4099731445312, + "y": -186.23599243164062 }, { - "x": 925.8150024414062, - "y": -186.9040069580078 + "x": 950.2620239257812, + "y": -182.7899932861328 }, { - "x": 946.4920043945312, - "y": -183.62899780273438 + "x": 968.697998046875, + "y": -177.5540008544922 }, { - "x": 966.7130126953125, - "y": -178.21099853515625 + "x": 986.5479736328125, + "y": -170.57899475097656 }, { - "x": 986.2570190429688, - "y": -170.70899963378906 + "x": 1003.6480102539062, + "y": -161.927001953125 }, { - "x": 1004.9099731445312, - "y": -161.2050018310547 + "x": 1019.8419799804688, + "y": -151.677001953125 }, { - "x": 1022.4669799804688, - "y": -149.80299377441406 + "x": 1034.98095703125, + "y": -139.9250030517578 }, { - "x": 1038.7359619140625, - "y": -136.6280059814453 + "x": 1048.925048828125, + "y": -126.77799987792969 }, { - "x": 1053.5389404296875, - "y": -121.82599639892578 + "x": 1061.5469970703125, + "y": -112.35700225830078 }, { - "x": 1066.7130126953125, - "y": -105.55699920654297 + "x": 1072.72998046875, + "y": -96.79399871826172 }, { - "x": 1078.114990234375, - "y": -88 + "x": 1082.373046875, + "y": -80.23100280761719 }, { - "x": 1087.6190185546875, - "y": -69.34700012207031 + "x": 1090.385986328125, + "y": -62.821998596191406 }, { - "x": 1095.1209716796875, - "y": -49.803001403808594 + "x": 1096.696044921875, + "y": -44.72600173950195 }, { - "x": 1100.5389404296875, - "y": -29.582000732421875 + "x": 1101.2449951171875, + "y": -26.108999252319336 }, { - "x": 1103.81396484375, - "y": -8.904999732971191 + "x": 1103.990966796875, + "y": -7.142000198364258 }, { "x": 1104.9100341796875, "y": 12 }, { - "x": 1103.81396484375, - "y": 32.904998779296875 + "x": 1103.990966796875, + "y": 31.142000198364258 }, { - "x": 1100.5389404296875, - "y": 53.582000732421875 + "x": 1101.2449951171875, + "y": 50.10900115966797 }, { - "x": 1095.1209716796875, - "y": 73.8030014038086 + "x": 1096.696044921875, + "y": 68.72599792480469 }, { - "x": 1087.6190185546875, - "y": 93.34700012207031 + "x": 1090.385986328125, + "y": 86.8219985961914 }, { - "x": 1078.114990234375, - "y": 111.9990005493164 + "x": 1082.373046875, + "y": 104.23100280761719 }, { - "x": 1066.7130126953125, - "y": 129.5570068359375 + "x": 1072.72998046875, + "y": 120.79399871826172 }, { - "x": 1053.5389404296875, - "y": 145.8260040283203 + "x": 1061.5469970703125, + "y": 136.35699462890625 }, { - "x": 1038.7359619140625, - "y": 160.6280059814453 + "x": 1048.925048828125, + "y": 150.7779998779297 }, { - "x": 1022.4669799804688, - "y": 173.80299377441406 + "x": 1034.98095703125, + "y": 163.9250030517578 }, { - "x": 1004.9099731445312, - "y": 185.2050018310547 + "x": 1019.8419799804688, + "y": 175.677001953125 }, { - "x": 986.2570190429688, - "y": 194.70899963378906 + "x": 1003.6480102539062, + "y": 185.927001953125 }, { - "x": 966.7130126953125, - "y": 202.21099853515625 + "x": 986.5479736328125, + "y": 194.57899475097656 }, { - "x": 946.4920043945312, - "y": 207.62899780273438 + "x": 968.697998046875, + "y": 201.5540008544922 }, { - "x": 925.8150024414062, - "y": 210.9040069580078 + "x": 950.2620239257812, + "y": 206.7899932861328 }, { - "x": 904.9099731445312, - "y": 212 + "x": 931.4099731445312, + "y": 210.23599243164062 } ], "isCurve": true, diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg index 19ee385912..9c63f2e3cf 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg @@ -1,9 +1,9 @@ -abcdabcab + .d2-142933150 .fill-N1{fill:#0A0F25;} + .d2-142933150 .fill-N2{fill:#676C7E;} + .d2-142933150 .fill-N3{fill:#9499AB;} + .d2-142933150 .fill-N4{fill:#CFD2DD;} + .d2-142933150 .fill-N5{fill:#DEE1EB;} + .d2-142933150 .fill-N6{fill:#EEF1F8;} + .d2-142933150 .fill-N7{fill:#FFFFFF;} + .d2-142933150 .fill-B1{fill:#0D32B2;} + .d2-142933150 .fill-B2{fill:#0D32B2;} + .d2-142933150 .fill-B3{fill:#E3E9FD;} + .d2-142933150 .fill-B4{fill:#E3E9FD;} + .d2-142933150 .fill-B5{fill:#EDF0FD;} + .d2-142933150 .fill-B6{fill:#F7F8FE;} + .d2-142933150 .fill-AA2{fill:#4A6FF3;} + .d2-142933150 .fill-AA4{fill:#EDF0FD;} + .d2-142933150 .fill-AA5{fill:#F7F8FE;} + .d2-142933150 .fill-AB4{fill:#EDF0FD;} + .d2-142933150 .fill-AB5{fill:#F7F8FE;} + .d2-142933150 .stroke-N1{stroke:#0A0F25;} + .d2-142933150 .stroke-N2{stroke:#676C7E;} + .d2-142933150 .stroke-N3{stroke:#9499AB;} + .d2-142933150 .stroke-N4{stroke:#CFD2DD;} + .d2-142933150 .stroke-N5{stroke:#DEE1EB;} + .d2-142933150 .stroke-N6{stroke:#EEF1F8;} + .d2-142933150 .stroke-N7{stroke:#FFFFFF;} + .d2-142933150 .stroke-B1{stroke:#0D32B2;} + .d2-142933150 .stroke-B2{stroke:#0D32B2;} + .d2-142933150 .stroke-B3{stroke:#E3E9FD;} + .d2-142933150 .stroke-B4{stroke:#E3E9FD;} + .d2-142933150 .stroke-B5{stroke:#EDF0FD;} + .d2-142933150 .stroke-B6{stroke:#F7F8FE;} + .d2-142933150 .stroke-AA2{stroke:#4A6FF3;} + .d2-142933150 .stroke-AA4{stroke:#EDF0FD;} + .d2-142933150 .stroke-AA5{stroke:#F7F8FE;} + .d2-142933150 .stroke-AB4{stroke:#EDF0FD;} + .d2-142933150 .stroke-AB5{stroke:#F7F8FE;} + .d2-142933150 .background-color-N1{background-color:#0A0F25;} + .d2-142933150 .background-color-N2{background-color:#676C7E;} + .d2-142933150 .background-color-N3{background-color:#9499AB;} + .d2-142933150 .background-color-N4{background-color:#CFD2DD;} + .d2-142933150 .background-color-N5{background-color:#DEE1EB;} + .d2-142933150 .background-color-N6{background-color:#EEF1F8;} + .d2-142933150 .background-color-N7{background-color:#FFFFFF;} + .d2-142933150 .background-color-B1{background-color:#0D32B2;} + .d2-142933150 .background-color-B2{background-color:#0D32B2;} + .d2-142933150 .background-color-B3{background-color:#E3E9FD;} + .d2-142933150 .background-color-B4{background-color:#E3E9FD;} + .d2-142933150 .background-color-B5{background-color:#EDF0FD;} + .d2-142933150 .background-color-B6{background-color:#F7F8FE;} + .d2-142933150 .background-color-AA2{background-color:#4A6FF3;} + .d2-142933150 .background-color-AA4{background-color:#EDF0FD;} + .d2-142933150 .background-color-AA5{background-color:#F7F8FE;} + .d2-142933150 .background-color-AB4{background-color:#EDF0FD;} + .d2-142933150 .background-color-AB5{background-color:#F7F8FE;} + .d2-142933150 .color-N1{color:#0A0F25;} + .d2-142933150 .color-N2{color:#676C7E;} + .d2-142933150 .color-N3{color:#9499AB;} + .d2-142933150 .color-N4{color:#CFD2DD;} + .d2-142933150 .color-N5{color:#DEE1EB;} + .d2-142933150 .color-N6{color:#EEF1F8;} + .d2-142933150 .color-N7{color:#FFFFFF;} + .d2-142933150 .color-B1{color:#0D32B2;} + .d2-142933150 .color-B2{color:#0D32B2;} + .d2-142933150 .color-B3{color:#E3E9FD;} + .d2-142933150 .color-B4{color:#E3E9FD;} + .d2-142933150 .color-B5{color:#EDF0FD;} + .d2-142933150 .color-B6{color:#F7F8FE;} + .d2-142933150 .color-AA2{color:#4A6FF3;} + .d2-142933150 .color-AA4{color:#EDF0FD;} + .d2-142933150 .color-AA5{color:#F7F8FE;} + .d2-142933150 .color-AB4{color:#EDF0FD;} + .d2-142933150 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-142933150);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-142933150);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-142933150);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-142933150);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-142933150);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-142933150);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-142933150);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-142933150);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>abcdabcab diff --git a/lib/geo/segment.go b/lib/geo/segment.go index ea29df98c9..3e9ed05967 100644 --- a/lib/geo/segment.go +++ b/lib/geo/segment.go @@ -126,3 +126,36 @@ func (segment Segment) Length() float64 { func (segment Segment) ToVector() Vector { return NewVector(segment.End.X-segment.Start.X, segment.End.Y-segment.Start.Y) } + +// IntersectCircle returns intersection points of this segment with a circle +// centered at the given point with the given radius. Returned points lie on +// the segment (parameter t in [0,1]). The list is empty when the segment +// misses the circle, or only touches it outside its endpoints. +func (s Segment) IntersectCircle(center *Point, radius float64) []*Point { + dx := s.End.X - s.Start.X + dy := s.End.Y - s.Start.Y + fx := s.Start.X - center.X + fy := s.Start.Y - center.Y + a := dx*dx + dy*dy + if a == 0 { + return nil + } + b := 2 * (fx*dx + fy*dy) + c := fx*fx + fy*fy - radius*radius + disc := b*b - 4*a*c + if disc < 0 { + return nil + } + sqrtDisc := math.Sqrt(disc) + var pts []*Point + for _, t := range []float64{(-b - sqrtDisc) / (2 * a), (-b + sqrtDisc) / (2 * a)} { + if t < 0 { + continue + } + if t > 1 { + continue + } + pts = append(pts, NewPoint(s.Start.X+t*dx, s.Start.Y+t*dy)) + } + return pts +} From 00778a69137fcea783cab42c6d09ac0dbdd56453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yasunori=20Morishima=EF=BC=88=E7=9B=9B=E5=B3=B6=E5=BA=B7?= =?UTF-8?q?=E5=BE=B3=EF=BC=89?= Date: Sat, 2 May 2026 06:43:19 +0000 Subject: [PATCH 4/8] lib/geo: unit tests for Segment.IntersectCircle --- lib/geo/segment_test.go | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/geo/segment_test.go b/lib/geo/segment_test.go index ce2836a7e5..37e7f22d25 100644 --- a/lib/geo/segment_test.go +++ b/lib/geo/segment_test.go @@ -31,3 +31,56 @@ func TestSegmentIntersections(t *testing.T) { intersections = s1.Intersections(*s5) assert.Equal(t, len(intersections), 0) } + +func TestSegmentIntersectCircle(t *testing.T) { + origin := NewPoint(0, 0) + + // segment passing through the origin → 2 intersections at (-r, 0) and (r, 0) + s := NewSegment(NewPoint(-10, 0), NewPoint(10, 0)) + pts := s.IntersectCircle(origin, 5) + assert.Equal(t, 2, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(-5, 0))) + assert.True(t, pts[1].Equals(NewPoint(5, 0))) + + // segment crossing the circle once with one endpoint inside + s = NewSegment(NewPoint(0, 0), NewPoint(10, 0)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 1, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(5, 0))) + + // segment entirely outside the circle, no crossing + s = NewSegment(NewPoint(10, 10), NewPoint(20, 20)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 0, len(pts)) + + // segment entirely inside the circle, no crossing + s = NewSegment(NewPoint(-1, 0), NewPoint(1, 0)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 0, len(pts)) + + // segment endpoint on the circle + s = NewSegment(NewPoint(0, 0), NewPoint(5, 0)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 1, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(5, 0))) + + // vertical segment chord intersecting the circle at two points + s = NewSegment(NewPoint(3, -10), NewPoint(3, 10)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 2, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(3, -4))) + assert.True(t, pts[1].Equals(NewPoint(3, 4))) + + // circle centered off origin + c := NewPoint(10, 10) + s = NewSegment(NewPoint(0, 10), NewPoint(20, 10)) + pts = s.IntersectCircle(c, 5) + assert.Equal(t, 2, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(5, 10))) + assert.True(t, pts[1].Equals(NewPoint(15, 10))) + + // degenerate zero-length segment returns nil + s = NewSegment(NewPoint(7, 7), NewPoint(7, 7)) + pts = s.IntersectCircle(origin, 5) + assert.Nil(t, pts) +} From bc6bb7e337c6ce23a101da2219c842e5ec1a7811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yasunori=20Morishima=EF=BC=88=E7=9B=9B=E5=B3=B6=E5=BA=B7?= =?UTF-8?q?=E5=BE=B3=EF=BC=89?= Date: Sat, 2 May 2026 08:09:19 +0000 Subject: [PATCH 5/8] d2cycle: trace to true shape border + dedup tangent intersections Addresses CodeRabbit review feedback on yasumorishima/d2#1. Major: my analytic intersection only used `geo.Box` edges, which is the shape's bounding box. For non-rectangular shapes (circle, oval, hexagon, cloud, ...) the bounding box border is not the actual shape outline, so connections were landing on the bounding box rather than on the shape. Fixed by passing each arc endpoint through `shape.TraceToShapeBorder`, which walks the line from the shape center to the box-border point and returns the intersection with the shape's perimeter (a no-op for rectangular shapes). Minor: `Segment.IntersectCircle` appended both quadratic roots even when the discriminant was zero (a tangent contact), so a graze was reported as two identical points. Fixed by emitting the second root only when the discriminant is strictly positive, and added a tangent regression test case. --- d2layouts/d2cycle/layout.go | 11 +++++++++++ lib/geo/segment.go | 11 ++++++++--- lib/geo/segment_test.go | 8 ++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index ad7226105c..0b2585881b 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -7,6 +7,7 @@ import ( "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/lib/geo" "oss.terrastruct.com/d2/lib/label" + "oss.terrastruct.com/d2/lib/shape" "oss.terrastruct.com/util-go/go2" ) @@ -109,6 +110,16 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { path = append(path, geo.NewPoint(radius*math.Cos(angle), radius*math.Sin(angle))) } + // path[0] / path[len-1] sit on the bounding-box border of the source / + // destination shape. For non-rectangular shapes (circle, oval, hexagon, + // cloud, ...) the bounding box border is not the shape border, so trace + // each endpoint inward from the shape center to the actual shape outline. + // TraceToShapeBorder is a no-op for rectangular shapes. + srcShape := edge.Src.ToShape() + dstShape := edge.Dst.ToShape() + path[0] = shape.TraceToShapeBorder(srcShape, path[0], srcCenter) + path[len(path)-1] = shape.TraceToShapeBorder(dstShape, path[len(path)-1], dstCenter) + edge.Route = path edge.IsCurve = true } diff --git a/lib/geo/segment.go b/lib/geo/segment.go index 3e9ed05967..c902ea890c 100644 --- a/lib/geo/segment.go +++ b/lib/geo/segment.go @@ -129,8 +129,9 @@ func (segment Segment) ToVector() Vector { // IntersectCircle returns intersection points of this segment with a circle // centered at the given point with the given radius. Returned points lie on -// the segment (parameter t in [0,1]). The list is empty when the segment -// misses the circle, or only touches it outside its endpoints. +// the segment (parameter t in [0,1]). A tangent contact yields a single +// point; the list is empty when the segment misses the circle, or only +// touches it outside its endpoints. func (s Segment) IntersectCircle(center *Point, radius float64) []*Point { dx := s.End.X - s.Start.X dy := s.End.Y - s.Start.Y @@ -147,8 +148,12 @@ func (s Segment) IntersectCircle(center *Point, radius float64) []*Point { return nil } sqrtDisc := math.Sqrt(disc) + roots := []float64{(-b - sqrtDisc) / (2 * a)} + if disc > 0 { + roots = append(roots, (-b+sqrtDisc)/(2*a)) + } var pts []*Point - for _, t := range []float64{(-b - sqrtDisc) / (2 * a), (-b + sqrtDisc) / (2 * a)} { + for _, t := range roots { if t < 0 { continue } diff --git a/lib/geo/segment_test.go b/lib/geo/segment_test.go index 37e7f22d25..0a2c9b963e 100644 --- a/lib/geo/segment_test.go +++ b/lib/geo/segment_test.go @@ -79,6 +79,14 @@ func TestSegmentIntersectCircle(t *testing.T) { assert.True(t, pts[0].Equals(NewPoint(5, 10))) assert.True(t, pts[1].Equals(NewPoint(15, 10))) + // tangent contact: the segment grazes the circle at a single point and + // must not be reported twice (regression test for the duplicated-root + // case when the discriminant is zero). + s = NewSegment(NewPoint(-10, 5), NewPoint(10, 5)) + pts = s.IntersectCircle(origin, 5) + assert.Equal(t, 1, len(pts)) + assert.True(t, pts[0].Equals(NewPoint(0, 5))) + // degenerate zero-length segment returns nil s = NewSegment(NewPoint(7, 7), NewPoint(7, 7)) pts = s.IntersectCircle(origin, 5) From e032e56a74a7fdcba67ec16e44bbb1f1b4ad87dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yasunori=20Morishima=EF=BC=88=E7=9B=9B=E5=B3=B6=E5=BA=B7?= =?UTF-8?q?=E5=BE=B3=EF=BC=89?= Date: Sat, 2 May 2026 09:03:15 +0000 Subject: [PATCH 6/8] d2cycle: clip fallback routes to shape borders too Addresses CodeRabbit second-review feedback on yasumorishima/d2#1. When `nextBoundaryAngle` could not find an arc-range crossing (very narrow sweep, degenerate geometry, ...) the previous fallbacks let `startAngle = srcAngle` / `endAngle = dstAngle` and emitted a route whose first/last point coincided with the shape center, since the center lies on the layout circle. That regressed the very fix the PR introduces and also fed `TraceToShapeBorder` a center-as-rectBorder input. Now the createCircularArc flow falls through to the straight-line fallback whenever either boundary angle is missing or the resulting range is empty, and `fallbackStraightRoute` itself runs each endpoint through a new `clipToShapeBorder` helper. The helper extends a ray from the shape center toward the other endpoint, intersects the bounding box, and refines via `TraceToShapeBorder` for non-rectangular shapes. Both fallbacks therefore emit shape-border endpoints, matching the arc-success path. The cycle-diagram fixture is unchanged because rectangle shapes never hit the fallback in the existing test cases. --- d2layouts/d2cycle/layout.go | 50 +++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index 0b2585881b..d3d89cc1b4 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -85,21 +85,17 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { dstAngle += 2 * math.Pi } sweep := dstAngle - srcAngle + srcShape := edge.Src.ToShape() + dstShape := edge.Dst.ToShape() if sweep <= 0 { - fallbackStraightRoute(edge, srcCenter, dstCenter) + fallbackStraightRoute(edge, srcShape, dstShape, srcCenter, dstCenter) return } startAngle, hasStart := nextBoundaryAngle(edge.Src.Box, origin, radius, srcAngle, sweep, true) - if !hasStart { - startAngle = srcAngle - } endAngle, hasEnd := nextBoundaryAngle(edge.Dst.Box, origin, radius, srcAngle, sweep, false) - if !hasEnd { - endAngle = dstAngle - } - if endAngle <= startAngle { - fallbackStraightRoute(edge, srcCenter, dstCenter) + if !hasStart || !hasEnd || endAngle <= startAngle { + fallbackStraightRoute(edge, srcShape, dstShape, srcCenter, dstCenter) return } @@ -115,8 +111,6 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { // cloud, ...) the bounding box border is not the shape border, so trace // each endpoint inward from the shape center to the actual shape outline. // TraceToShapeBorder is a no-op for rectangular shapes. - srcShape := edge.Src.ToShape() - dstShape := edge.Dst.ToShape() path[0] = shape.TraceToShapeBorder(srcShape, path[0], srcCenter) path[len(path)-1] = shape.TraceToShapeBorder(dstShape, path[len(path)-1], dstCenter) @@ -124,11 +118,41 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { edge.IsCurve = true } -func fallbackStraightRoute(edge *d2graph.Edge, srcCenter, dstCenter *geo.Point) { - edge.Route = []*geo.Point{srcCenter, dstCenter} +// fallbackStraightRoute renders a straight connection whose endpoints are +// clipped to the source and destination shape borders along the line between +// the two centers, used when the analytic arc geometry degenerates (zero +// sweep, no boundary crossing in the arc range, etc.). +func fallbackStraightRoute(edge *d2graph.Edge, srcShape, dstShape shape.Shape, srcCenter, dstCenter *geo.Point) { + srcBorder := clipToShapeBorder(srcShape, edge.Src.Box, srcCenter, dstCenter) + dstBorder := clipToShapeBorder(dstShape, edge.Dst.Box, dstCenter, srcCenter) + edge.Route = []*geo.Point{srcBorder, dstBorder} edge.IsCurve = false } +// clipToShapeBorder returns the point where a ray from `from` (assumed inside +// `box`) toward `toward` first exits the actual shape outline. The bounding +// box is consulted first to obtain a rectangular border point, then the shape +// helper refines it for non-rectangular shapes. +func clipToShapeBorder(shp shape.Shape, box *geo.Box, from, toward *geo.Point) *geo.Point { + dx := toward.X - from.X + dy := toward.Y - from.Y + dist := math.Hypot(dx, dy) + if dist == 0 { + return from + } + // Extend the ray well past `toward` so the segment definitely exits the + // box even when `toward` itself sits inside the box. + diag := math.Hypot(box.Width, box.Height) + scale := (dist + 2*diag) / dist + extended := geo.NewPoint(from.X+dx*scale, from.Y+dy*scale) + + rectBorder := extended + if pts := box.Intersections(geo.Segment{Start: from, End: extended}); len(pts) > 0 { + rectBorder = pts[0] + } + return shape.TraceToShapeBorder(shp, rectBorder, from) +} + // nextBoundaryAngle scans the angles where the layout circle crosses an edge // of the box. When forSrc is true it returns the smallest such angle strictly // greater than srcAngle (the point where the arc exits the source box). When From 2181ee3a5328ae9f3b97b6bb22964463ac5ba710 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Wed, 29 Jul 2026 13:10:26 +0900 Subject: [PATCH 7/8] e2etests: regenerate cycle-diagram goldens after merge --- .../txtar/cycle-diagram/dagre/board.exp.json | 1 + .../txtar/cycle-diagram/dagre/sketch.exp.svg | 12 ++---------- .../testdata/txtar/cycle-diagram/elk/board.exp.json | 1 + .../testdata/txtar/cycle-diagram/elk/sketch.exp.svg | 12 ++---------- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json index ab992ed244..a74da10e86 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -10,6 +10,7 @@ }, "isFolderOnly": false, "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", "shapes": [ { "id": "1", diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg index a1bfb352db..13128a2684 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg @@ -1,4 +1,4 @@ -abcdabcab - - - - - - - - - + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json index f1c964734b..10f5db1aaf 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -10,6 +10,7 @@ }, "isFolderOnly": false, "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", "shapes": [ { "id": "1", diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg index 9c63f2e3cf..ab9e7b8c70 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg @@ -1,4 +1,4 @@ -abcdabcab - - - - - - - - - + \ No newline at end of file From 20300673258e9ab5befb882ce293d74f2c20fd4d Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Wed, 29 Jul 2026 13:10:26 +0900 Subject: [PATCH 8/8] test(geo): compare IntersectCircle results with a tolerance Point.Equals compares float64 exactly, so the chord case asserted y == 4 against a computed 3.9999999999999982 and failed. --- lib/geo/segment_test.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/geo/segment_test.go b/lib/geo/segment_test.go index 0a2c9b963e..afd8399a5a 100644 --- a/lib/geo/segment_test.go +++ b/lib/geo/segment_test.go @@ -39,14 +39,17 @@ func TestSegmentIntersectCircle(t *testing.T) { s := NewSegment(NewPoint(-10, 0), NewPoint(10, 0)) pts := s.IntersectCircle(origin, 5) assert.Equal(t, 2, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(-5, 0))) - assert.True(t, pts[1].Equals(NewPoint(5, 0))) + assert.InDelta(t, -5, pts[0].X, 1e-9) + assert.InDelta(t, 0, pts[0].Y, 1e-9) + assert.InDelta(t, 5, pts[1].X, 1e-9) + assert.InDelta(t, 0, pts[1].Y, 1e-9) // segment crossing the circle once with one endpoint inside s = NewSegment(NewPoint(0, 0), NewPoint(10, 0)) pts = s.IntersectCircle(origin, 5) assert.Equal(t, 1, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(5, 0))) + assert.InDelta(t, 5, pts[0].X, 1e-9) + assert.InDelta(t, 0, pts[0].Y, 1e-9) // segment entirely outside the circle, no crossing s = NewSegment(NewPoint(10, 10), NewPoint(20, 20)) @@ -62,22 +65,27 @@ func TestSegmentIntersectCircle(t *testing.T) { s = NewSegment(NewPoint(0, 0), NewPoint(5, 0)) pts = s.IntersectCircle(origin, 5) assert.Equal(t, 1, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(5, 0))) + assert.InDelta(t, 5, pts[0].X, 1e-9) + assert.InDelta(t, 0, pts[0].Y, 1e-9) // vertical segment chord intersecting the circle at two points s = NewSegment(NewPoint(3, -10), NewPoint(3, 10)) pts = s.IntersectCircle(origin, 5) assert.Equal(t, 2, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(3, -4))) - assert.True(t, pts[1].Equals(NewPoint(3, 4))) + assert.InDelta(t, 3, pts[0].X, 1e-9) + assert.InDelta(t, -4, pts[0].Y, 1e-9) + assert.InDelta(t, 3, pts[1].X, 1e-9) + assert.InDelta(t, 4, pts[1].Y, 1e-9) // circle centered off origin c := NewPoint(10, 10) s = NewSegment(NewPoint(0, 10), NewPoint(20, 10)) pts = s.IntersectCircle(c, 5) assert.Equal(t, 2, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(5, 10))) - assert.True(t, pts[1].Equals(NewPoint(15, 10))) + assert.InDelta(t, 5, pts[0].X, 1e-9) + assert.InDelta(t, 10, pts[0].Y, 1e-9) + assert.InDelta(t, 15, pts[1].X, 1e-9) + assert.InDelta(t, 10, pts[1].Y, 1e-9) // tangent contact: the segment grazes the circle at a single point and // must not be reported twice (regression test for the duplicated-root @@ -85,7 +93,8 @@ func TestSegmentIntersectCircle(t *testing.T) { s = NewSegment(NewPoint(-10, 5), NewPoint(10, 5)) pts = s.IntersectCircle(origin, 5) assert.Equal(t, 1, len(pts)) - assert.True(t, pts[0].Equals(NewPoint(0, 5))) + assert.InDelta(t, 0, pts[0].X, 1e-9) + assert.InDelta(t, 5, pts[0].Y, 1e-9) // degenerate zero-length segment returns nil s = NewSegment(NewPoint(7, 7), NewPoint(7, 7))