From 94a20083cdb2ff893ad419fb5f4f731031ea157e Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 19 Feb 2025 08:04:01 -0700 Subject: [PATCH 01/17] 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 02/17] 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 7bd47a2e1f27378d529029498c5c6c356e34b833 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 03/17] 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 9bf7c388a2ee78cb64b21f3ea56b9ee87e5aa1d4 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 04/17] 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 1cced2d9a57313e2f5ee718fcb71711ea9a83a6e 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 05/17] 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 f9f65e583d829012460f28cec56d562d0c4c6df2 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 06/17] 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 b45b74f5fb091d191d98eab0a53e91c1d027aaa5 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Wed, 29 Jul 2026 13:10:26 +0900 Subject: [PATCH 07/17] 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 942d0b83b937791745769f3ce4cda8ae600e0f29 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Wed, 29 Jul 2026 13:10:26 +0900 Subject: [PATCH 08/17] 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)) From 243ca2babb47ccbc87a704ebd48597de076df3f0 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Sun, 6 Sep 2026 09:14:17 +0900 Subject: [PATCH 09/17] Render the cycle shape in the scene builder master added a raster/scene path that draws each shape from an explicit type switch, with a default branch that asks lib/shape for path commands. Square-family shapes have no path commands, so the cycle container fell through and failed with "unsupported typed geometry for cycle" in TestTargetShapePixels. Handle cycle next to sequence_diagram and hierarchy in the four square-family switches, add it to the shape lists the two scene builder tests enumerate, and regenerate the target-shapes golden. --- .../d2raster/target_pixel_coverage_test.go | 1 + .../d2raster/testdata/target-shapes.exp.png | Bin 90965 -> 94619 bytes d2renderers/d2scenebuild/shape_effects.go | 2 +- d2renderers/d2scenebuild/sketch_shapes.go | 6 +++--- .../d2scenebuild/sketch_shapes_test.go | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/d2renderers/d2raster/target_pixel_coverage_test.go b/d2renderers/d2raster/target_pixel_coverage_test.go index 5146d4ad3c..efc2b01751 100644 --- a/d2renderers/d2raster/target_pixel_coverage_test.go +++ b/d2renderers/d2raster/target_pixel_coverage_test.go @@ -56,6 +56,7 @@ func TestTargetShapePixels(t *testing.T) { d2target.ShapeSQLTable, d2target.ShapeImage, d2target.ShapeSequenceDiagram, + d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, } if !reflect.DeepEqual(shapeValues, d2target.Shapes) { diff --git a/d2renderers/d2raster/testdata/target-shapes.exp.png b/d2renderers/d2raster/testdata/target-shapes.exp.png index c7126219f90bd73ddced5a4e708b75d7845e35d7..85f894820e50229ac485b7c96b3fe8b706bb1d93 100644 GIT binary patch delta 12936 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 diff --git a/d2renderers/d2scenebuild/shape_effects.go b/d2renderers/d2scenebuild/shape_effects.go index 8d241ec1a1..ec30c3a7d8 100644 --- a/d2renderers/d2scenebuild/shape_effects.go +++ b/d2renderers/d2scenebuild/shape_effects.go @@ -295,7 +295,7 @@ func (b *builder) buildOrdinaryShapeOutline(targetShape d2target.Shape, fill d2s Fill: fill, Stroke: stroke, })) - case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, "": + case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, "": radius := clampBorderRadius(float64(targetShape.BorderRadius), box.Width, box.Height) nodes = append(nodes, d2scene.NewNode(d2scene.Rect{ Box: box, diff --git a/d2renderers/d2scenebuild/sketch_shapes.go b/d2renderers/d2scenebuild/sketch_shapes.go index 9f1b1f15fe..56accc2ff1 100644 --- a/d2renderers/d2scenebuild/sketch_shapes.go +++ b/d2renderers/d2scenebuild/sketch_shapes.go @@ -127,7 +127,7 @@ func (b *builder) buildSketchOrdinaryShape(targetShape d2target.Shape, idPrefix object, idPrefix, fillRaw, strokeRaw, strokeWidth, dashes, dashOffset, drawable, d2scene.Translate(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)), targetShape, ) - case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, "": + case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, "": drawable := newSketchGenerator().Rectangle(0, 0, float64(targetShape.Width), float64(targetShape.Height), options) nodes, err = b.compileSketchShapeDrawable( object, idPrefix, fillRaw, strokeRaw, strokeWidth, dashes, dashOffset, @@ -156,7 +156,7 @@ func (b *builder) buildSketchOrdinaryShape(targetShape d2target.Shape, idPrefix RadiusX: float64(targetShape.Width) / 2, RadiusY: float64(targetShape.Height) / 2, }) - case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, "": + case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, "": overlayPrimitives = append(overlayPrimitives, d2scene.Rect{Box: sketchShapeBox(targetShape)}) default: for _, node := range roughNodes { @@ -578,7 +578,7 @@ func (b *builder) buildSketchOutlineOnly(targetShape d2target.Shape, idPrefix st ), d2scene.Translate(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)), targetShape, ) - case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, "": + case d2target.ShapeRectangle, d2target.ShapeSquare, d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, "": return b.compileSketchShapeDrawable( object, idPrefix, fillRaw, strokeRaw, strokeWidth, dashes, dashOffset, newSketchGenerator().Rectangle(0, 0, float64(targetShape.Width), float64(targetShape.Height), options), diff --git a/d2renderers/d2scenebuild/sketch_shapes_test.go b/d2renderers/d2scenebuild/sketch_shapes_test.go index 18040f1263..8792075369 100644 --- a/d2renderers/d2scenebuild/sketch_shapes_test.go +++ b/d2renderers/d2scenebuild/sketch_shapes_test.go @@ -28,7 +28,7 @@ func TestSketchOrdinaryShapeFamiliesUseTypedRoughPathsDeterministically(t *testi d2target.ShapeStep, d2target.ShapeCallout, d2target.ShapeStoredData, d2target.ShapePerson, d2target.ShapeC4Person, d2target.ShapeDiamond, d2target.ShapeHexagon, d2target.ShapeCloud, - d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, + d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, } for _, shapeType := range shapeTypes { name := shapeType From 56c182386c04c129b78789aff5c30b6b748d4fda Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Sun, 6 Sep 2026 13:19:27 +0900 Subject: [PATCH 10/17] Place the cycle board at the origin and draw its container The scene builder commit made the cycle container visible for the first time, which showed that the container box does not sit on the board it wraps: d2layouts fits the container to the nested board and then places it at (0, 0), and PositionNested offsets the contents by the container position alone, so a nested board is expected to start at the origin. positionObjects centers the ring on (0, 0) instead, leaving the upper left half of the board negative, so the container was drawn offset by half the ring. Normalise the board at the end of the layout, including the arcs in the bounds so the container encloses them, and record the size on the root for FitToGraph. The shift is a translation applied to the shapes and the routes together, so the arc endpoints stay on the shape borders: the first arc keeps x = 28.476319 + 227 = 255.476319 exactly. Handle cycle in the SVG rectangle case as well, next to sequence_diagram and hierarchy, so SVG and raster agree on the container. --- d2layouts/d2cycle/layout.go | 48 ++ d2renderers/d2svg/d2svg.go | 2 +- .../txtar/cycle-diagram/dagre/board.exp.json | 786 ++++++++--------- .../txtar/cycle-diagram/dagre/sketch.exp.svg | 22 +- .../txtar/cycle-diagram/elk/board.exp.json | 788 +++++++++--------- .../txtar/cycle-diagram/elk/sketch.exp.svg | 22 +- 6 files changed, 858 insertions(+), 810 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index e947d624b3..5dc1386f70 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -36,9 +36,57 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) e createCircularArc(edge, radius) } + normalizeToOrigin(g) + return nil } +// normalizeToOrigin shifts the laid out board so that everything it draws -- +// the shapes and the arcs between them -- lies at or below the origin corner, +// and records the resulting size on the root. positionObjects centers the ring +// on (0, 0), which leaves the upper left half of the board in negative +// coordinates, but d2layouts fits the container to the nested board and then +// places it at (0, 0), and d2layouts.PositionNested offsets the contents by the +// container position alone -- both assume the nested board already starts at +// the origin. Without this the container is drawn offset from the very children +// it wraps. The arcs are included in the bounds so the container encloses them +// rather than being clipped to the ring of shapes. +func normalizeToOrigin(g *d2graph.Graph) { + minX, minY := math.Inf(1), math.Inf(1) + maxX, maxY := math.Inf(-1), math.Inf(-1) + track := func(x, y float64) { + minX, minY = math.Min(minX, x), math.Min(minY, y) + maxX, maxY = math.Max(maxX, x), math.Max(maxY, y) + } + for _, obj := range g.Objects { + if obj.TopLeft == nil { + continue + } + track(obj.TopLeft.X, obj.TopLeft.Y) + track(obj.TopLeft.X+obj.Width, obj.TopLeft.Y+obj.Height) + } + for _, edge := range g.Edges { + for _, p := range edge.Route { + track(p.X, p.Y) + } + } + if math.IsInf(minX, 1) || math.IsInf(minY, 1) { + return + } + + dx, dy := -minX, -minY + if dx != 0 || dy != 0 { + for _, obj := range g.Root.ChildrenArray { + obj.MoveWithDescendants(dx, dy) + } + for _, edge := range g.Edges { + edge.Move(dx, dy) + } + } + g.Root.Width = maxX - minX + g.Root.Height = maxY - minY +} + func calculateRadius(objects []*d2graph.Object) float64 { numObjects := float64(len(objects)) maxSize := 0.0 diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 96fbccefe7..82f1e31d12 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -1809,7 +1809,7 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape fmt.Fprint(writer, el.Render()) // TODO should standardize "" to rectangle - case d2target.ShapeRectangle, d2target.ShapeSequenceDiagram, d2target.ShapeHierarchy, "": + case d2target.ShapeRectangle, d2target.ShapeSequenceDiagram, d2target.ShapeCycleDiagram, d2target.ShapeHierarchy, "": borderRadius := math.MaxFloat64 if targetShape.BorderRadius != 0 { borderRadius = float64(targetShape.BorderRadius) diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json index a74da10e86..bd2e258286 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -57,8 +57,8 @@ "id": "1.a", "type": "rectangle", "pos": { - "x": -26, - "y": -233 + "x": 200, + "y": 0 }, "width": 53, "height": 66, @@ -99,8 +99,8 @@ "id": "1.b", "type": "rectangle", "pos": { - "x": 173, - "y": -33 + "x": 400, + "y": 200 }, "width": 53, "height": 66, @@ -141,8 +141,8 @@ "id": "1.c", "type": "rectangle", "pos": { - "x": -26, - "y": 167 + "x": 200, + "y": 400 }, "width": 53, "height": 66, @@ -183,8 +183,8 @@ "id": "1.d", "type": "rectangle", "pos": { - "x": -227, - "y": -32 + "x": 0, + "y": 200 }, "width": 54, "height": 66, @@ -226,10 +226,10 @@ "type": "cycle", "pos": { "x": 513, - "y": 50 + "y": 17 }, "width": 399, - "height": 366, + "height": 433, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -266,8 +266,8 @@ "id": "2.a", "type": "rectangle", "pos": { - "x": 486, - "y": -183 + "x": 686, + "y": 17 }, "width": 53, "height": 66, @@ -308,8 +308,8 @@ "id": "2.b", "type": "rectangle", "pos": { - "x": 659, - "y": 116 + "x": 859, + "y": 317 }, "width": 53, "height": 66, @@ -350,8 +350,8 @@ "id": "2.c", "type": "rectangle", "pos": { - "x": 313, - "y": 117 + "x": 513, + "y": 317 }, "width": 53, "height": 66, @@ -395,7 +395,7 @@ "x": 972, "y": 0 }, - "width": 53, + "width": 226, "height": 466, "opacity": 1, "strokeDash": 0, @@ -433,8 +433,8 @@ "id": "3.a", "type": "rectangle", "pos": { - "x": 945, - "y": -233 + "x": 972, + "y": 0 }, "width": 53, "height": 66, @@ -475,8 +475,8 @@ "id": "3.b", "type": "rectangle", "pos": { - "x": 945, - "y": 167 + "x": 972, + "y": 400 }, "width": 53, "height": 66, @@ -541,128 +541,128 @@ "link": "", "route": [ { - "x": 26.5, - "y": -198.23599243164062 + "x": 253.5, + "y": 34.76300048828125 }, { - "x": 34.87900161743164, - "y": -196.93499755859375 + "x": 261.8789978027344, + "y": 36.06399917602539 }, { - "x": 43.196998596191406, - "y": -195.2790069580078 + "x": 270.1969909667969, + "y": 37.720001220703125 }, { - "x": 51.43600082397461, - "y": -193.27200317382812 + "x": 278.4360046386719, + "y": 39.72700119018555 }, { - "x": 59.58300018310547, - "y": -190.91799926757812 + "x": 286.5830078125, + "y": 42.08100128173828 }, { - "x": 67.62300109863281, - "y": -188.22000122070312 + "x": 294.62298583984375, + "y": 44.77899932861328 }, { - "x": 75.54100036621094, - "y": -185.1840057373047 + "x": 302.5409851074219, + "y": 47.814998626708984 }, { - "x": 83.3239974975586, - "y": -181.8159942626953 + "x": 310.3240051269531, + "y": 51.18299865722656 }, { - "x": 90.95600128173828, - "y": -178.1199951171875 + "x": 317.95599365234375, + "y": 54.87900161743164 }, { - "x": 98.4260025024414, - "y": -174.10400390625 + "x": 325.4259948730469, + "y": 58.89500045776367 }, { - "x": 105.71800231933594, - "y": -169.77499389648438 + "x": 332.7179870605469, + "y": 63.2239990234375 }, { - "x": 112.81999969482422, - "y": -165.14100646972656 + "x": 339.82000732421875, + "y": 67.85800170898438 }, { - "x": 119.71900177001953, - "y": -160.20899963378906 + "x": 346.718994140625, + "y": 72.79000091552734 }, { - "x": 126.40299987792969, - "y": -154.99000549316406 + "x": 353.40301513671875, + "y": 78.00900268554688 }, { - "x": 132.86000061035156, - "y": -149.49200439453125 + "x": 359.8599853515625, + "y": 83.50700378417969 }, { - "x": 139.0780029296875, - "y": -143.7259979248047 + "x": 366.0780029296875, + "y": 89.27300262451172 }, { - "x": 145.04600524902344, - "y": -137.7010040283203 + "x": 372.0459899902344, + "y": 95.2979965209961 }, { - "x": 150.7530059814453, - "y": -131.42799377441406 + "x": 377.75299072265625, + "y": 101.57099914550781 }, { - "x": 156.18899536132812, - "y": -124.91899871826172 + "x": 383.1889953613281, + "y": 108.08000183105469 }, { - "x": 161.343994140625, - "y": -118.18599700927734 + "x": 388.343994140625, + "y": 114.81300354003906 }, { - "x": 166.20899963378906, - "y": -111.23999786376953 + "x": 393.2090148925781, + "y": 121.75900268554688 }, { - "x": 170.77499389648438, - "y": -104.09400177001953 + "x": 397.7749938964844, + "y": 128.90499877929688 }, { - "x": 175.03500366210938, - "y": -96.76100158691406 + "x": 402.0350036621094, + "y": 136.23800659179688 }, { - "x": 178.97900390625, - "y": -89.25299835205078 + "x": 405.97900390625, + "y": 143.74600219726562 }, { - "x": 182.6020050048828, - "y": -81.58599853515625 + "x": 409.60198974609375, + "y": 151.41299438476562 }, { - "x": 185.89599609375, - "y": -73.77200317382812 + "x": 412.89599609375, + "y": 159.2270050048828 }, { - "x": 188.85699462890625, - "y": -65.82499694824219 + "x": 415.85699462890625, + "y": 167.1739959716797 }, { - "x": 191.4770050048828, - "y": -57.75899887084961 + "x": 418.47698974609375, + "y": 175.24000549316406 }, { - "x": 193.75399780273438, - "y": -49.59000015258789 + "x": 420.7539978027344, + "y": 183.40899658203125 }, { - "x": 195.6820068359375, - "y": -41.332000732421875 + "x": 422.6820068359375, + "y": 191.66700744628906 }, { - "x": 197.25799560546875, - "y": -33 + "x": 424.25799560546875, + "y": 200 } ], "isCurve": true, @@ -697,128 +697,128 @@ "link": "", "route": [ { - "x": 197.25799560546875, - "y": 32.999000549316406 + "x": 424.25799560546875, + "y": 266 }, { - "x": 195.6820068359375, - "y": 41.332000732421875 + "x": 422.6820068359375, + "y": 274.3320007324219 }, { - "x": 193.75399780273438, - "y": 49.59000015258789 + "x": 420.7539978027344, + "y": 282.5899963378906 }, { - "x": 191.4770050048828, - "y": 57.75899887084961 + "x": 418.47698974609375, + "y": 290.7590026855469 }, { - "x": 188.85699462890625, - "y": 65.82499694824219 + "x": 415.85699462890625, + "y": 298.82501220703125 }, { - "x": 185.89599609375, - "y": 73.77200317382812 + "x": 412.89599609375, + "y": 306.7720031738281 }, { - "x": 182.6020050048828, - "y": 81.58599853515625 + "x": 409.60198974609375, + "y": 314.58599853515625 }, { - "x": 178.97900390625, - "y": 89.25299835205078 + "x": 405.97900390625, + "y": 322.25299072265625 }, { - "x": 175.03500366210938, - "y": 96.76100158691406 + "x": 402.0350036621094, + "y": 329.760986328125 }, { - "x": 170.77499389648438, - "y": 104.09400177001953 + "x": 397.7749938964844, + "y": 337.093994140625 }, { - "x": 166.20899963378906, - "y": 111.23999786376953 + "x": 393.2090148925781, + "y": 344.239990234375 }, { - "x": 161.343994140625, - "y": 118.18599700927734 + "x": 388.343994140625, + "y": 351.1860046386719 }, { - "x": 156.18899536132812, - "y": 124.91899871826172 + "x": 383.1889953613281, + "y": 357.91900634765625 }, { - "x": 150.7530059814453, - "y": 131.42799377441406 + "x": 377.75299072265625, + "y": 364.4280090332031 }, { - "x": 145.04600524902344, - "y": 137.7010040283203 + "x": 372.0459899902344, + "y": 370.70098876953125 }, { - "x": 139.0780029296875, - "y": 143.7259979248047 + "x": 366.0780029296875, + "y": 376.72601318359375 }, { - "x": 132.86000061035156, - "y": 149.49200439453125 + "x": 359.8599853515625, + "y": 382.49200439453125 }, { - "x": 126.40299987792969, - "y": 154.99000549316406 + "x": 353.40301513671875, + "y": 387.989990234375 }, { - "x": 119.71900177001953, - "y": 160.20899963378906 + "x": 346.718994140625, + "y": 393.2090148925781 }, { - "x": 112.81999969482422, - "y": 165.14100646972656 + "x": 339.82000732421875, + "y": 398.1409912109375 }, { - "x": 105.71800231933594, - "y": 169.77499389648438 + "x": 332.7179870605469, + "y": 402.7749938964844 }, { - "x": 98.4260025024414, - "y": 174.10400390625 + "x": 325.4259948730469, + "y": 407.10400390625 }, { - "x": 90.95600128173828, - "y": 178.1199951171875 + "x": 317.95599365234375, + "y": 411.1199951171875 }, { - "x": 83.3239974975586, - "y": 181.8159942626953 + "x": 310.3240051269531, + "y": 414.8160095214844 }, { - "x": 75.54100036621094, - "y": 185.1840057373047 + "x": 302.5409851074219, + "y": 418.1839904785156 }, { - "x": 67.62300109863281, - "y": 188.22000122070312 + "x": 294.62298583984375, + "y": 421.2200012207031 }, { - "x": 59.58300018310547, - "y": 190.91799926757812 + "x": 286.5830078125, + "y": 423.9179992675781 }, { - "x": 51.43600082397461, - "y": 193.27200317382812 + "x": 278.4360046386719, + "y": 426.2720031738281 }, { - "x": 43.196998596191406, - "y": 195.2790069580078 + "x": 270.1969909667969, + "y": 428.27899169921875 }, { - "x": 34.87900161743164, - "y": 196.93499755859375 + "x": 261.8789978027344, + "y": 429.93499755859375 }, { - "x": 26.5, - "y": 198.23599243164062 + "x": 253.5, + "y": 431.2359924316406 } ], "isCurve": true, @@ -853,128 +853,128 @@ "link": "", "route": [ { - "x": -26.499000549316406, - "y": 198.23599243164062 + "x": 200.5, + "y": 431.2359924316406 }, { - "x": -34.87900161743164, - "y": 196.93499755859375 + "x": 192.1199951171875, + "y": 429.93499755859375 }, { - "x": -43.196998596191406, - "y": 195.2790069580078 + "x": 183.802001953125, + "y": 428.27899169921875 }, { - "x": -51.43600082397461, - "y": 193.27200317382812 + "x": 175.56300354003906, + "y": 426.2720031738281 }, { - "x": -59.58300018310547, - "y": 190.91799926757812 + "x": 167.41600036621094, + "y": 423.9179992675781 }, { - "x": -67.62300109863281, - "y": 188.22000122070312 + "x": 159.37600708007812, + "y": 421.2200012207031 }, { - "x": -75.54100036621094, - "y": 185.1840057373047 + "x": 151.45799255371094, + "y": 418.1839904785156 }, { - "x": -83.3239974975586, - "y": 181.8159942626953 + "x": 143.6750030517578, + "y": 414.8160095214844 }, { - "x": -90.95600128173828, - "y": 178.1199951171875 + "x": 136.04299926757812, + "y": 411.1199951171875 }, { - "x": -98.4260025024414, - "y": 174.10400390625 + "x": 128.572998046875, + "y": 407.10400390625 }, { - "x": -105.71800231933594, - "y": 169.77499389648438 + "x": 121.28099822998047, + "y": 402.7749938964844 }, { - "x": -112.81999969482422, - "y": 165.14100646972656 + "x": 114.17900085449219, + "y": 398.1409912109375 }, { - "x": -119.71900177001953, - "y": 160.20899963378906 + "x": 107.27999877929688, + "y": 393.2090148925781 }, { - "x": -126.40299987792969, - "y": 154.99000549316406 + "x": 100.59600067138672, + "y": 387.989990234375 }, { - "x": -132.86000061035156, - "y": 149.49200439453125 + "x": 94.13899993896484, + "y": 382.49200439453125 }, { - "x": -139.0780029296875, - "y": 143.7259979248047 + "x": 87.9209976196289, + "y": 376.72601318359375 }, { - "x": -145.04600524902344, - "y": 137.7010040283203 + "x": 81.9530029296875, + "y": 370.70098876953125 }, { - "x": -150.7530059814453, - "y": 131.42799377441406 + "x": 76.24600219726562, + "y": 364.4280090332031 }, { - "x": -156.18899536132812, - "y": 124.91899871826172 + "x": 70.80999755859375, + "y": 357.91900634765625 }, { - "x": -161.343994140625, - "y": 118.18599700927734 + "x": 65.65499877929688, + "y": 351.1860046386719 }, { - "x": -166.20899963378906, - "y": 111.23999786376953 + "x": 60.790000915527344, + "y": 344.239990234375 }, { - "x": -170.77499389648438, - "y": 104.09400177001953 + "x": 56.2239990234375, + "y": 337.093994140625 }, { - "x": -175.03500366210938, - "y": 96.76100158691406 + "x": 51.9640007019043, + "y": 329.760986328125 }, { - "x": -178.97900390625, - "y": 89.25299835205078 + "x": 48.02000045776367, + "y": 322.25299072265625 }, { - "x": -182.6020050048828, - "y": 81.58599853515625 + "x": 44.39699935913086, + "y": 314.58599853515625 }, { - "x": -185.89599609375, - "y": 73.77200317382812 + "x": 41.10300064086914, + "y": 306.7720031738281 }, { - "x": -188.85699462890625, - "y": 65.82499694824219 + "x": 38.141998291015625, + "y": 298.82501220703125 }, { - "x": -191.4770050048828, - "y": 57.75899887084961 + "x": 35.52199935913086, + "y": 290.7590026855469 }, { - "x": -193.75399780273438, - "y": 49.59000015258789 + "x": 33.244998931884766, + "y": 282.5899963378906 }, { - "x": -195.6820068359375, - "y": 41.332000732421875 + "x": 31.316999435424805, + "y": 274.3320007324219 }, { - "x": -197.25799560546875, - "y": 33 + "x": 29.740999221801758, + "y": 266 } ], "isCurve": true, @@ -1009,128 +1009,128 @@ "link": "", "route": [ { - "x": 539.5, - "y": -148.23599243164062 + "x": 739.2050170898438, + "y": 51.76300048828125 }, { - "x": 551.2050170898438, - "y": -146.3159942626953 + "x": 750.9099731445312, + "y": 53.68299865722656 }, { - "x": 562.7760009765625, - "y": -143.70599365234375 + "x": 762.4810180664062, + "y": 56.292999267578125 }, { - "x": 574.1719970703125, - "y": -140.4149932861328 + "x": 773.8770141601562, + "y": 59.58399963378906 }, { - "x": 585.3519897460938, - "y": -136.4530029296875 + "x": 785.0570068359375, + "y": 63.54600143432617 }, { - "x": 596.2780151367188, - "y": -131.83599853515625 + "x": 795.9829711914062, + "y": 68.16300201416016 }, { - "x": 606.9119873046875, - "y": -126.58000183105469 + "x": 806.6170043945312, + "y": 73.41899871826172 }, { - "x": 617.2150268554688, - "y": -120.7020034790039 + "x": 816.9199829101562, + "y": 79.2969970703125 }, { - "x": 627.1510009765625, - "y": -114.2229995727539 + "x": 826.8560180664062, + "y": 85.7760009765625 }, { - "x": 636.6859741210938, - "y": -107.16699981689453 + "x": 836.3909912109375, + "y": 92.83200073242188 }, { - "x": 645.7849731445312, - "y": -99.55899810791016 + "x": 845.489990234375, + "y": 100.44000244140625 }, { - "x": 654.4180297851562, - "y": -91.42400360107422 + "x": 854.1229858398438, + "y": 108.57499694824219 }, { - "x": 662.552978515625, - "y": -82.79100036621094 + "x": 862.2579956054688, + "y": 117.20800018310547 }, { - "x": 670.1619873046875, - "y": -73.69200134277344 + "x": 869.8670043945312, + "y": 126.30699920654297 }, { - "x": 677.218994140625, - "y": -64.15699768066406 + "x": 876.9240112304688, + "y": 135.8419952392578 }, { - "x": 683.697021484375, - "y": -54.22100067138672 + "x": 883.4030151367188, + "y": 145.7779998779297 }, { - "x": 689.5759887695312, - "y": -43.91899871826172 + "x": 889.281005859375, + "y": 156.0800018310547 }, { - "x": 694.8330078125, - "y": -33.2859992980957 + "x": 894.5380249023438, + "y": 166.71299743652344 }, { - "x": 699.4509887695312, - "y": -22.360000610351562 + "x": 899.156005859375, + "y": 177.63900756835938 }, { - "x": 703.4119873046875, - "y": -11.178999900817871 + "x": 903.1170043945312, + "y": 188.82000732421875 }, { - "x": 706.7039794921875, - "y": 0.2150000035762787 + "x": 906.4089965820312, + "y": 200.21499633789062 }, { - "x": 709.3150024414062, - "y": 11.78600025177002 + "x": 909.02001953125, + "y": 211.78599548339844 }, { - "x": 711.2349853515625, - "y": 23.492000579833984 + "x": 910.9400024414062, + "y": 223.49200439453125 }, { - "x": 712.4580078125, - "y": 35.290000915527344 + "x": 912.1630249023438, + "y": 235.2899932861328 }, { - "x": 712.97900390625, - "y": 47.13999938964844 + "x": 912.6840209960938, + "y": 247.13999938964844 }, { - "x": 712.7969970703125, - "y": 59 + "x": 912.5020141601562, + "y": 259 }, { - "x": 711.9119873046875, - "y": 70.8290023803711 + "x": 911.6170043945312, + "y": 270.8290100097656 }, { - "x": 710.3270263671875, - "y": 82.58399963378906 + "x": 910.031982421875, + "y": 282.5840148925781 }, { - "x": 708.0479736328125, - "y": 94.2249984741211 + "x": 907.7540283203125, + "y": 294.2250061035156 }, { - "x": 705.083984375, - "y": 105.70999908447266 + "x": 904.7890014648438, + "y": 305.7099914550781 }, { - "x": 701.4429931640625, - "y": 116.9990005493164 + "x": 901.1480102539062, + "y": 317 } ], "isCurve": true, @@ -1165,128 +1165,128 @@ "link": "", "route": [ { - "x": 662.3679809570312, - "y": 182.99899291992188 + "x": 862.072998046875, + "y": 383 }, { - "x": 654.6589965820312, - "y": 191.1820068359375 + "x": 854.364013671875, + "y": 391.1820068359375 }, { - "x": 646.5020141601562, - "y": 198.91900634765625 + "x": 846.2069702148438, + "y": 398.91900634765625 }, { - "x": 637.9229736328125, - "y": 206.18600463867188 + "x": 837.6279907226562, + "y": 406.1860046386719 }, { - "x": 628.9500122070312, - "y": 212.95799255371094 + "x": 828.655029296875, + "y": 412.9580078125 }, { - "x": 619.6099853515625, - "y": 219.21600341796875 + "x": 819.3150024414062, + "y": 419.21600341796875 }, { - "x": 609.9329833984375, - "y": 224.93899536132812 + "x": 809.6380004882812, + "y": 424.9389953613281 }, { - "x": 599.9500122070312, - "y": 230.11000061035156 + "x": 799.655029296875, + "y": 430.1099853515625 }, { - "x": 589.6920166015625, - "y": 234.71099853515625 + "x": 789.39697265625, + "y": 434.71099853515625 }, { - "x": 579.1920166015625, - "y": 238.72799682617188 + "x": 778.89697265625, + "y": 438.7279968261719 }, { - "x": 568.4819946289062, - "y": 242.14999389648438 + "x": 768.18701171875, + "y": 442.1499938964844 }, { - "x": 557.5980224609375, - "y": 244.96400451660156 + "x": 757.302978515625, + "y": 444.9639892578125 }, { - "x": 546.572021484375, - "y": 247.16200256347656 + "x": 746.2769775390625, + "y": 447.1619873046875 }, { - "x": 535.4400024414062, - "y": 248.73699951171875 + "x": 735.14501953125, + "y": 448.73699951171875 }, { - "x": 524.2379760742188, - "y": 249.6840057373047 + "x": 723.9429931640625, + "y": 449.6839904785156 }, { - "x": 513, - "y": 250 + "x": 712.7050170898438, + "y": 450 }, { - "x": 501.760986328125, - "y": 249.6840057373047 + "x": 701.4660034179688, + "y": 449.6839904785156 }, { - "x": 490.5589904785156, - "y": 248.73699951171875 + "x": 690.2639770507812, + "y": 448.73699951171875 }, { - "x": 479.427001953125, - "y": 247.16200256347656 + "x": 679.1320190429688, + "y": 447.1619873046875 }, { - "x": 468.4010009765625, - "y": 244.96400451660156 + "x": 668.1069946289062, + "y": 444.9639892578125 }, { - "x": 457.5169982910156, - "y": 242.14999389648438 + "x": 657.2219848632812, + "y": 442.1499938964844 }, { - "x": 446.8070068359375, - "y": 238.72799682617188 + "x": 646.5130004882812, + "y": 438.7279968261719 }, { - "x": 436.3070068359375, - "y": 234.71099853515625 + "x": 636.0120239257812, + "y": 434.71099853515625 }, { - "x": 426.04901123046875, - "y": 230.11000061035156 + "x": 625.7540283203125, + "y": 430.1099853515625 }, { - "x": 416.0660095214844, - "y": 224.93899536132812 + "x": 615.77099609375, + "y": 424.9389953613281 }, { - "x": 406.3890075683594, - "y": 219.21600341796875 + "x": 606.094970703125, + "y": 419.21600341796875 }, { - "x": 397.04901123046875, - "y": 212.95799255371094 + "x": 596.7550048828125, + "y": 412.9580078125 }, { - "x": 388.07598876953125, - "y": 206.18600463867188 + "x": 587.781005859375, + "y": 406.1860046386719 }, { - "x": 379.49700927734375, - "y": 198.91900634765625 + "x": 579.2020263671875, + "y": 398.91900634765625 }, { - "x": 371.3399963378906, - "y": 191.1820068359375 + "x": 571.0449829101562, + "y": 391.1820068359375 }, { - "x": 363.6310119628906, - "y": 183 + "x": 563.3359985351562, + "y": 383 } ], "isCurve": true, @@ -1321,128 +1321,128 @@ "link": "", "route": [ { - "x": 998.5, - "y": -198.23599243164062 + "x": 1025, + "y": 34.76300048828125 }, { - "x": 1017.3519897460938, - "y": -194.7899932861328 + "x": 1043.85205078125, + "y": 38.20899963378906 }, { - "x": 1035.7879638671875, - "y": -189.5540008544922 + "x": 1062.2879638671875, + "y": 43.44499969482422 }, { - "x": 1053.637939453125, - "y": -182.57899475097656 + "x": 1080.137939453125, + "y": 50.41999816894531 }, { - "x": 1070.738037109375, - "y": -173.927001953125 + "x": 1097.238037109375, + "y": 59.071998596191406 }, { - "x": 1086.9320068359375, - "y": -163.677001953125 + "x": 1113.4320068359375, + "y": 69.3219985961914 }, { - "x": 1102.071044921875, - "y": -151.9250030517578 + "x": 1128.571044921875, + "y": 81.0739974975586 }, { - "x": 1116.0150146484375, - "y": -138.7779998779297 + "x": 1142.5150146484375, + "y": 94.22100067138672 }, { - "x": 1128.636962890625, - "y": -124.35700225830078 + "x": 1155.136962890625, + "y": 108.64199829101562 }, { - "x": 1139.8199462890625, - "y": -108.79399871826172 + "x": 1166.3199462890625, + "y": 124.20500183105469 }, { - "x": 1149.4630126953125, - "y": -92.23100280761719 + "x": 1175.9630126953125, + "y": 140.76800537109375 }, { - "x": 1157.4759521484375, - "y": -74.8219985961914 + "x": 1183.9759521484375, + "y": 158.177001953125 }, { - "x": 1163.7860107421875, - "y": -56.72600173950195 + "x": 1190.2860107421875, + "y": 176.2729949951172 }, { - "x": 1168.3349609375, - "y": -38.10900115966797 + "x": 1194.8349609375, + "y": 194.88999938964844 }, { - "x": 1171.0810546875, - "y": -19.142000198364258 + "x": 1197.5810546875, + "y": 213.85699462890625 }, { - "x": 1172, - "y": 0 + "x": 1198.5, + "y": 233 }, { - "x": 1171.0810546875, - "y": 19.142000198364258 + "x": 1197.5810546875, + "y": 252.14199829101562 }, { - "x": 1168.3349609375, - "y": 38.10900115966797 + "x": 1194.8349609375, + "y": 271.1090087890625 }, { - "x": 1163.7860107421875, - "y": 56.72600173950195 + "x": 1190.2860107421875, + "y": 289.72601318359375 }, { - "x": 1157.4759521484375, - "y": 74.8219985961914 + "x": 1183.9759521484375, + "y": 307.8219909667969 }, { - "x": 1149.4630126953125, - "y": 92.23100280761719 + "x": 1175.9630126953125, + "y": 325.2309875488281 }, { - "x": 1139.8199462890625, - "y": 108.79399871826172 + "x": 1166.3199462890625, + "y": 341.79400634765625 }, { - "x": 1128.636962890625, - "y": 124.35700225830078 + "x": 1155.136962890625, + "y": 357.35699462890625 }, { - "x": 1116.0150146484375, - "y": 138.7779998779297 + "x": 1142.5150146484375, + "y": 371.77801513671875 }, { - "x": 1102.071044921875, - "y": 151.9250030517578 + "x": 1128.571044921875, + "y": 384.92498779296875 }, { - "x": 1086.9320068359375, - "y": 163.677001953125 + "x": 1113.4320068359375, + "y": 396.677001953125 }, { - "x": 1070.738037109375, - "y": 173.927001953125 + "x": 1097.238037109375, + "y": 406.927001953125 }, { - "x": 1053.637939453125, - "y": 182.57899475097656 + "x": 1080.137939453125, + "y": 415.5790100097656 }, { - "x": 1035.7879638671875, - "y": 189.5540008544922 + "x": 1062.2879638671875, + "y": 422.5539855957031 }, { - "x": 1017.3519897460938, - "y": 194.7899932861328 + "x": 1043.85205078125, + "y": 427.7900085449219 }, { - "x": 998.5, - "y": 198.23599243164062 + "x": 1025, + "y": 431.2359924316406 } ], "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 249cce571a..4dd629edfa 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-2975240071 .fill-N1{fill:#0A0F25;} + .d2-2975240071 .fill-N7{fill:#FFFFFF;} + .d2-2975240071 .fill-B1{fill:#0D32B2;} + .d2-2975240071 .fill-B5{fill:#EDF0FD;} + .d2-2975240071 .stroke-B1{stroke:#0D32B2;} + .d2-2975240071 .stroke-B2{stroke:#0D32B2;}.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;}.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 10f5db1aaf..dfd05143bb 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -57,8 +57,8 @@ "id": "1.a", "type": "rectangle", "pos": { - "x": -14, - "y": -221 + "x": 212, + "y": 12 }, "width": 53, "height": 66, @@ -99,8 +99,8 @@ "id": "1.b", "type": "rectangle", "pos": { - "x": 185, - "y": -21 + "x": 412, + "y": 212 }, "width": 53, "height": 66, @@ -141,8 +141,8 @@ "id": "1.c", "type": "rectangle", "pos": { - "x": -14, - "y": 179 + "x": 212, + "y": 412 }, "width": 53, "height": 66, @@ -183,8 +183,8 @@ "id": "1.d", "type": "rectangle", "pos": { - "x": -215, - "y": -20 + "x": 12, + "y": 212 }, "width": 54, "height": 66, @@ -226,10 +226,10 @@ "type": "cycle", "pos": { "x": 485, - "y": 61 + "y": 28 }, "width": 400, - "height": 367, + "height": 433, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -266,8 +266,8 @@ "id": "2.a", "type": "rectangle", "pos": { - "x": 459, - "y": -171 + "x": 658, + "y": 28 }, "width": 53, "height": 66, @@ -308,8 +308,8 @@ "id": "2.b", "type": "rectangle", "pos": { - "x": 632, - "y": 128 + "x": 831, + "y": 328 }, "width": 53, "height": 66, @@ -350,8 +350,8 @@ "id": "2.c", "type": "rectangle", "pos": { - "x": 285, - "y": 129 + "x": 485, + "y": 328 }, "width": 53, "height": 66, @@ -392,10 +392,10 @@ "id": "3", "type": "cycle", "pos": { - "x": 904, + "x": 905, "y": 12 }, - "width": 53, + "width": 227, "height": 466, "opacity": 1, "strokeDash": 0, @@ -433,8 +433,8 @@ "id": "3.a", "type": "rectangle", "pos": { - "x": 878, - "y": -221 + "x": 905, + "y": 12 }, "width": 53, "height": 66, @@ -475,8 +475,8 @@ "id": "3.b", "type": "rectangle", "pos": { - "x": 878, - "y": 179 + "x": 905, + "y": 412 }, "width": 53, "height": 66, @@ -541,128 +541,128 @@ "link": "", "route": [ { - "x": 38.5, - "y": -186.23599243164062 + "x": 265.5, + "y": 46.76300048828125 }, { - "x": 46.87900161743164, - "y": -184.93499755859375 + "x": 273.8789978027344, + "y": 48.06399917602539 }, { - "x": 55.196998596191406, - "y": -183.2790069580078 + "x": 282.1969909667969, + "y": 49.720001220703125 }, { - "x": 63.43600082397461, - "y": -181.27200317382812 + "x": 290.4360046386719, + "y": 51.72700119018555 }, { - "x": 71.58300018310547, - "y": -178.91799926757812 + "x": 298.5830078125, + "y": 54.08100128173828 }, { - "x": 79.62300109863281, - "y": -176.22000122070312 + "x": 306.62298583984375, + "y": 56.77899932861328 }, { - "x": 87.54100036621094, - "y": -173.1840057373047 + "x": 314.5409851074219, + "y": 59.814998626708984 }, { - "x": 95.3239974975586, - "y": -169.8159942626953 + "x": 322.3240051269531, + "y": 63.18299865722656 }, { - "x": 102.95600128173828, - "y": -166.1199951171875 + "x": 329.95599365234375, + "y": 66.87899780273438 }, { - "x": 110.4260025024414, - "y": -162.10400390625 + "x": 337.4259948730469, + "y": 70.8949966430664 }, { - "x": 117.71800231933594, - "y": -157.77499389648438 + "x": 344.7179870605469, + "y": 75.2239990234375 }, { - "x": 124.81999969482422, - "y": -153.14100646972656 + "x": 351.82000732421875, + "y": 79.85800170898438 }, { - "x": 131.718994140625, - "y": -148.20899963378906 + "x": 358.718994140625, + "y": 84.79000091552734 }, { - "x": 138.4029998779297, - "y": -142.99000549316406 + "x": 365.40301513671875, + "y": 90.00900268554688 }, { - "x": 144.86000061035156, - "y": -137.49200439453125 + "x": 371.8599853515625, + "y": 95.50700378417969 }, { - "x": 151.0780029296875, - "y": -131.7259979248047 + "x": 378.0780029296875, + "y": 101.27300262451172 }, { - "x": 157.04600524902344, - "y": -125.70099639892578 + "x": 384.0459899902344, + "y": 107.2979965209961 }, { - "x": 162.7530059814453, - "y": -119.4280014038086 + "x": 389.75299072265625, + "y": 113.57099914550781 }, { - "x": 168.18899536132812, - "y": -112.91899871826172 + "x": 395.1889953613281, + "y": 120.08000183105469 }, { - "x": 173.343994140625, - "y": -106.18599700927734 + "x": 400.343994140625, + "y": 126.81300354003906 }, { - "x": 178.20899963378906, - "y": -99.23999786376953 + "x": 405.2090148925781, + "y": 133.75900268554688 }, { - "x": 182.77499389648438, - "y": -92.09400177001953 + "x": 409.7749938964844, + "y": 140.90499877929688 }, { - "x": 187.03500366210938, - "y": -84.76100158691406 + "x": 414.0350036621094, + "y": 148.23800659179688 }, { - "x": 190.97900390625, - "y": -77.25299835205078 + "x": 417.97900390625, + "y": 155.74600219726562 }, { - "x": 194.6020050048828, - "y": -69.58599853515625 + "x": 421.60198974609375, + "y": 163.41299438476562 }, { - "x": 197.89599609375, - "y": -61.77199935913086 + "x": 424.89599609375, + "y": 171.2270050048828 }, { - "x": 200.85699462890625, - "y": -53.82500076293945 + "x": 427.85699462890625, + "y": 179.1739959716797 }, { - "x": 203.4770050048828, - "y": -45.75899887084961 + "x": 430.47698974609375, + "y": 187.24000549316406 }, { - "x": 205.75399780273438, - "y": -37.59000015258789 + "x": 432.7539978027344, + "y": 195.40899658203125 }, { - "x": 207.6820068359375, - "y": -29.332000732421875 + "x": 434.6820068359375, + "y": 203.66700744628906 }, { - "x": 209.25799560546875, - "y": -21 + "x": 436.25799560546875, + "y": 212 } ], "isCurve": true, @@ -697,128 +697,128 @@ "link": "", "route": [ { - "x": 209.25799560546875, - "y": 44.999000549316406 + "x": 436.25799560546875, + "y": 278 }, { - "x": 207.6820068359375, - "y": 53.332000732421875 + "x": 434.6820068359375, + "y": 286.3320007324219 }, { - "x": 205.75399780273438, - "y": 61.59000015258789 + "x": 432.7539978027344, + "y": 294.5899963378906 }, { - "x": 203.4770050048828, - "y": 69.75900268554688 + "x": 430.47698974609375, + "y": 302.7590026855469 }, { - "x": 200.85699462890625, - "y": 77.82499694824219 + "x": 427.85699462890625, + "y": 310.82501220703125 }, { - "x": 197.89599609375, - "y": 85.77200317382812 + "x": 424.89599609375, + "y": 318.7720031738281 }, { - "x": 194.6020050048828, - "y": 93.58599853515625 + "x": 421.60198974609375, + "y": 326.58599853515625 }, { - "x": 190.97900390625, - "y": 101.25299835205078 + "x": 417.97900390625, + "y": 334.25299072265625 }, { - "x": 187.03500366210938, - "y": 108.76100158691406 + "x": 414.0350036621094, + "y": 341.760986328125 }, { - "x": 182.77499389648438, - "y": 116.09400177001953 + "x": 409.7749938964844, + "y": 349.093994140625 }, { - "x": 178.20899963378906, - "y": 123.23999786376953 + "x": 405.2090148925781, + "y": 356.239990234375 }, { - "x": 173.343994140625, - "y": 130.18600463867188 + "x": 400.343994140625, + "y": 363.1860046386719 }, { - "x": 168.18899536132812, - "y": 136.91900634765625 + "x": 395.1889953613281, + "y": 369.91900634765625 }, { - "x": 162.7530059814453, - "y": 143.42799377441406 + "x": 389.75299072265625, + "y": 376.4280090332031 }, { - "x": 157.04600524902344, - "y": 149.7010040283203 + "x": 384.0459899902344, + "y": 382.70098876953125 }, { - "x": 151.0780029296875, - "y": 155.7259979248047 + "x": 378.0780029296875, + "y": 388.72601318359375 }, { - "x": 144.86000061035156, - "y": 161.49200439453125 + "x": 371.8599853515625, + "y": 394.49200439453125 }, { - "x": 138.4029998779297, - "y": 166.99000549316406 + "x": 365.40301513671875, + "y": 399.989990234375 }, { - "x": 131.718994140625, - "y": 172.20899963378906 + "x": 358.718994140625, + "y": 405.2090148925781 }, { - "x": 124.81999969482422, - "y": 177.14100646972656 + "x": 351.82000732421875, + "y": 410.1409912109375 }, { - "x": 117.71800231933594, - "y": 181.77499389648438 + "x": 344.7179870605469, + "y": 414.7749938964844 }, { - "x": 110.4260025024414, - "y": 186.10400390625 + "x": 337.4259948730469, + "y": 419.10400390625 }, { - "x": 102.95600128173828, - "y": 190.1199951171875 + "x": 329.95599365234375, + "y": 423.1199951171875 }, { - "x": 95.3239974975586, - "y": 193.8159942626953 + "x": 322.3240051269531, + "y": 426.8160095214844 }, { - "x": 87.54100036621094, - "y": 197.1840057373047 + "x": 314.5409851074219, + "y": 430.1839904785156 }, { - "x": 79.62300109863281, - "y": 200.22000122070312 + "x": 306.62298583984375, + "y": 433.2200012207031 }, { - "x": 71.58300018310547, - "y": 202.91799926757812 + "x": 298.5830078125, + "y": 435.9179992675781 }, { - "x": 63.43600082397461, - "y": 205.27200317382812 + "x": 290.4360046386719, + "y": 438.2720031738281 }, { - "x": 55.196998596191406, - "y": 207.2790069580078 + "x": 282.1969909667969, + "y": 440.27899169921875 }, { - "x": 46.87900161743164, - "y": 208.93499755859375 + "x": 273.8789978027344, + "y": 441.93499755859375 }, { - "x": 38.5, - "y": 210.23599243164062 + "x": 265.5, + "y": 443.2359924316406 } ], "isCurve": true, @@ -853,128 +853,128 @@ "link": "", "route": [ { - "x": -14.49899959564209, - "y": 210.23599243164062 + "x": 212.5, + "y": 443.2359924316406 }, { - "x": -22.878999710083008, - "y": 208.93499755859375 + "x": 204.1199951171875, + "y": 441.93499755859375 }, { - "x": -31.19700050354004, - "y": 207.2790069580078 + "x": 195.802001953125, + "y": 440.27899169921875 }, { - "x": -39.43600082397461, - "y": 205.27200317382812 + "x": 187.56300354003906, + "y": 438.2720031738281 }, { - "x": -47.58300018310547, - "y": 202.91799926757812 + "x": 179.41600036621094, + "y": 435.9179992675781 }, { - "x": -55.62300109863281, - "y": 200.22000122070312 + "x": 171.37600708007812, + "y": 433.2200012207031 }, { - "x": -63.54100036621094, - "y": 197.1840057373047 + "x": 163.45799255371094, + "y": 430.1839904785156 }, { - "x": -71.3239974975586, - "y": 193.8159942626953 + "x": 155.6750030517578, + "y": 426.8160095214844 }, { - "x": -78.95600128173828, - "y": 190.1199951171875 + "x": 148.04299926757812, + "y": 423.1199951171875 }, { - "x": -86.4260025024414, - "y": 186.10400390625 + "x": 140.572998046875, + "y": 419.10400390625 }, { - "x": -93.71800231933594, - "y": 181.77499389648438 + "x": 133.281005859375, + "y": 414.7749938964844 }, { - "x": -100.81999969482422, - "y": 177.14100646972656 + "x": 126.17900085449219, + "y": 410.1409912109375 }, { - "x": -107.71900177001953, - "y": 172.20899963378906 + "x": 119.27999877929688, + "y": 405.2090148925781 }, { - "x": -114.40299987792969, - "y": 166.99000549316406 + "x": 112.59600067138672, + "y": 399.989990234375 }, { - "x": -120.86000061035156, - "y": 161.49200439453125 + "x": 106.13899993896484, + "y": 394.49200439453125 }, { - "x": -127.0780029296875, - "y": 155.7259979248047 + "x": 99.9209976196289, + "y": 388.72601318359375 }, { - "x": -133.04600524902344, - "y": 149.7010040283203 + "x": 93.9530029296875, + "y": 382.70098876953125 }, { - "x": -138.7530059814453, - "y": 143.42799377441406 + "x": 88.24600219726562, + "y": 376.4280090332031 }, { - "x": -144.18899536132812, - "y": 136.91900634765625 + "x": 82.80999755859375, + "y": 369.91900634765625 }, { - "x": -149.343994140625, - "y": 130.18600463867188 + "x": 77.65499877929688, + "y": 363.1860046386719 }, { - "x": -154.20899963378906, - "y": 123.23999786376953 + "x": 72.79000091552734, + "y": 356.239990234375 }, { - "x": -158.77499389648438, - "y": 116.09400177001953 + "x": 68.2239990234375, + "y": 349.093994140625 }, { - "x": -163.03500366210938, - "y": 108.76100158691406 + "x": 63.9640007019043, + "y": 341.760986328125 }, { - "x": -166.97900390625, - "y": 101.25299835205078 + "x": 60.02000045776367, + "y": 334.25299072265625 }, { - "x": -170.6020050048828, - "y": 93.58599853515625 + "x": 56.39699935913086, + "y": 326.58599853515625 }, { - "x": -173.89599609375, - "y": 85.77200317382812 + "x": 53.10300064086914, + "y": 318.7720031738281 }, { - "x": -176.85699462890625, - "y": 77.82499694824219 + "x": 50.141998291015625, + "y": 310.82501220703125 }, { - "x": -179.4770050048828, - "y": 69.75900268554688 + "x": 47.52199935913086, + "y": 302.7590026855469 }, { - "x": -181.75399780273438, - "y": 61.59000015258789 + "x": 45.244998931884766, + "y": 294.5899963378906 }, { - "x": -183.6820068359375, - "y": 53.332000732421875 + "x": 43.31700134277344, + "y": 286.3320007324219 }, { - "x": -185.25799560546875, - "y": 45 + "x": 41.74100112915039, + "y": 278 } ], "isCurve": true, @@ -1009,128 +1009,128 @@ "link": "", "route": [ { - "x": 512, - "y": -136.23599243164062 + "x": 711.7050170898438, + "y": 63.26300048828125 }, { - "x": 523.7050170898438, - "y": -134.3159942626953 + "x": 723.4099731445312, + "y": 65.18299865722656 }, { - "x": 535.2760009765625, - "y": -131.70599365234375 + "x": 734.9810180664062, + "y": 67.79299926757812 }, { - "x": 546.6719970703125, - "y": -128.4149932861328 + "x": 746.3770141601562, + "y": 71.08399963378906 }, { - "x": 557.8519897460938, - "y": -124.4530029296875 + "x": 757.5570068359375, + "y": 75.0459976196289 }, { - "x": 568.7780151367188, - "y": -119.83599853515625 + "x": 768.4829711914062, + "y": 79.66300201416016 }, { - "x": 579.4119873046875, - "y": -114.58000183105469 + "x": 779.1170043945312, + "y": 84.91899871826172 }, { - "x": 589.7150268554688, - "y": -108.7020034790039 + "x": 789.4199829101562, + "y": 90.7969970703125 }, { - "x": 599.6510009765625, - "y": -102.2229995727539 + "x": 799.3560180664062, + "y": 97.2760009765625 }, { - "x": 609.1859741210938, - "y": -95.16699981689453 + "x": 808.8909912109375, + "y": 104.33200073242188 }, { - "x": 618.2849731445312, - "y": -87.55899810791016 + "x": 817.989990234375, + "y": 111.94000244140625 }, { - "x": 626.9180297851562, - "y": -79.42400360107422 + "x": 826.6229858398438, + "y": 120.07499694824219 }, { - "x": 635.052978515625, - "y": -70.79100036621094 + "x": 834.7579956054688, + "y": 128.70799255371094 }, { - "x": 642.6619873046875, - "y": -61.69200134277344 + "x": 842.3670043945312, + "y": 137.8070068359375 }, { - "x": 649.718994140625, - "y": -52.15700149536133 + "x": 849.4240112304688, + "y": 147.3419952392578 }, { - "x": 656.197021484375, - "y": -42.22100067138672 + "x": 855.9030151367188, + "y": 157.2779998779297 }, { - "x": 662.0759887695312, - "y": -31.91900062561035 + "x": 861.781005859375, + "y": 167.5800018310547 }, { - "x": 667.3330078125, - "y": -21.285999298095703 + "x": 867.0380249023438, + "y": 178.21299743652344 }, { - "x": 671.9509887695312, - "y": -10.359999656677246 + "x": 871.656005859375, + "y": 189.13900756835938 }, { - "x": 675.9119873046875, - "y": 0.8199999928474426 + "x": 875.6170043945312, + "y": 200.32000732421875 }, { - "x": 679.2039794921875, - "y": 12.21500015258789 + "x": 878.9089965820312, + "y": 211.71499633789062 }, { - "x": 681.8150024414062, - "y": 23.785999298095703 + "x": 881.52001953125, + "y": 223.28599548339844 }, { - "x": 683.7349853515625, - "y": 35.492000579833984 + "x": 883.4400024414062, + "y": 234.99200439453125 }, { - "x": 684.9580078125, - "y": 47.290000915527344 + "x": 884.6630249023438, + "y": 246.7899932861328 }, { - "x": 685.47900390625, - "y": 59.13999938964844 + "x": 885.1840209960938, + "y": 258.6400146484375 }, { - "x": 685.2969970703125, - "y": 71 + "x": 885.0020141601562, + "y": 270.5 }, { - "x": 684.4119873046875, - "y": 82.8290023803711 + "x": 884.1170043945312, + "y": 282.3290100097656 }, { - "x": 682.8270263671875, - "y": 94.58399963378906 + "x": 882.531982421875, + "y": 294.0840148925781 }, { - "x": 680.5479736328125, - "y": 106.2249984741211 + "x": 880.2540283203125, + "y": 305.7250061035156 }, { - "x": 677.583984375, - "y": 117.70999908447266 + "x": 877.2890014648438, + "y": 317.2099914550781 }, { - "x": 673.9429931640625, - "y": 128.99899291992188 + "x": 873.6480102539062, + "y": 328.5 } ], "isCurve": true, @@ -1165,128 +1165,128 @@ "link": "", "route": [ { - "x": 634.8679809570312, - "y": 194.99899291992188 + "x": 834.572998046875, + "y": 394.5 }, { - "x": 627.1589965820312, - "y": 203.1820068359375 + "x": 826.864013671875, + "y": 402.6820068359375 }, { - "x": 619.0020141601562, - "y": 210.91900634765625 + "x": 818.7069702148438, + "y": 410.41900634765625 }, { - "x": 610.4229736328125, - "y": 218.18600463867188 + "x": 810.1279907226562, + "y": 417.6860046386719 }, { - "x": 601.4500122070312, - "y": 224.95799255371094 + "x": 801.155029296875, + "y": 424.4580078125 }, { - "x": 592.1099853515625, - "y": 231.21600341796875 + "x": 791.8150024414062, + "y": 430.71600341796875 }, { - "x": 582.4329833984375, - "y": 236.93899536132812 + "x": 782.1380004882812, + "y": 436.4389953613281 }, { - "x": 572.4500122070312, - "y": 242.11000061035156 + "x": 772.155029296875, + "y": 441.6099853515625 }, { - "x": 562.1920166015625, - "y": 246.71099853515625 + "x": 761.89697265625, + "y": 446.21099853515625 }, { - "x": 551.6920166015625, - "y": 250.72799682617188 + "x": 751.39697265625, + "y": 450.2279968261719 }, { - "x": 540.9819946289062, - "y": 254.14999389648438 + "x": 740.68701171875, + "y": 453.6499938964844 }, { - "x": 530.0980224609375, - "y": 256.9639892578125 + "x": 729.802978515625, + "y": 456.4639892578125 }, { - "x": 519.072021484375, - "y": 259.1619873046875 + "x": 718.7769775390625, + "y": 458.6619873046875 }, { - "x": 507.94000244140625, - "y": 260.73699951171875 + "x": 707.64501953125, + "y": 460.23699951171875 }, { - "x": 496.7380065917969, - "y": 261.6839904785156 + "x": 696.4429931640625, + "y": 461.1839904785156 }, { - "x": 485.5, - "y": 262 + "x": 685.2050170898438, + "y": 461.5 }, { - "x": 474.260986328125, - "y": 261.6839904785156 + "x": 673.9660034179688, + "y": 461.1839904785156 }, { - "x": 463.0589904785156, - "y": 260.73699951171875 + "x": 662.7639770507812, + "y": 460.23699951171875 }, { - "x": 451.927001953125, - "y": 259.1619873046875 + "x": 651.6320190429688, + "y": 458.6619873046875 }, { - "x": 440.9010009765625, - "y": 256.9639892578125 + "x": 640.6069946289062, + "y": 456.4639892578125 }, { - "x": 430.0169982910156, - "y": 254.14999389648438 + "x": 629.7219848632812, + "y": 453.6499938964844 }, { - "x": 419.3070068359375, - "y": 250.72799682617188 + "x": 619.0130004882812, + "y": 450.2279968261719 }, { - "x": 408.8070068359375, - "y": 246.71099853515625 + "x": 608.5120239257812, + "y": 446.21099853515625 }, { - "x": 398.54901123046875, - "y": 242.11000061035156 + "x": 598.2540283203125, + "y": 441.6099853515625 }, { - "x": 388.5660095214844, - "y": 236.93899536132812 + "x": 588.27099609375, + "y": 436.4389953613281 }, { - "x": 378.8890075683594, - "y": 231.21600341796875 + "x": 578.594970703125, + "y": 430.71600341796875 }, { - "x": 369.54901123046875, - "y": 224.95799255371094 + "x": 569.2550048828125, + "y": 424.4580078125 }, { - "x": 360.57598876953125, - "y": 218.18600463867188 + "x": 560.281005859375, + "y": 417.6860046386719 }, { - "x": 351.99700927734375, - "y": 210.91900634765625 + "x": 551.7020263671875, + "y": 410.41900634765625 }, { - "x": 343.8399963378906, - "y": 203.1820068359375 + "x": 543.5449829101562, + "y": 402.6820068359375 }, { - "x": 336.1310119628906, - "y": 195 + "x": 535.8359985351562, + "y": 394.5 } ], "isCurve": true, @@ -1321,128 +1321,128 @@ "link": "", "route": [ { - "x": 931.4099731445312, - "y": -186.23599243164062 + "x": 958.1840209960938, + "y": 46.76300048828125 }, { - "x": 950.2620239257812, - "y": -182.7899932861328 + "x": 977.0360107421875, + "y": 50.20899963378906 }, { - "x": 968.697998046875, - "y": -177.5540008544922 + "x": 995.4719848632812, + "y": 55.44499969482422 }, { - "x": 986.5479736328125, - "y": -170.57899475097656 + "x": 1013.322021484375, + "y": 62.41999816894531 }, { - "x": 1003.6480102539062, - "y": -161.927001953125 + "x": 1030.4229736328125, + "y": 71.0719985961914 }, { - "x": 1019.8419799804688, - "y": -151.677001953125 + "x": 1046.616943359375, + "y": 81.3219985961914 }, { - "x": 1034.98095703125, - "y": -139.9250030517578 + "x": 1061.7550048828125, + "y": 93.0739974975586 }, { - "x": 1048.925048828125, - "y": -126.77799987792969 + "x": 1075.699951171875, + "y": 106.22100067138672 }, { - "x": 1061.5469970703125, - "y": -112.35700225830078 + "x": 1088.321044921875, + "y": 120.64199829101562 }, { - "x": 1072.72998046875, - "y": -96.79399871826172 + "x": 1099.5050048828125, + "y": 136.2050018310547 }, { - "x": 1082.373046875, - "y": -80.23100280761719 + "x": 1109.14794921875, + "y": 152.76800537109375 }, { - "x": 1090.385986328125, - "y": -62.821998596191406 + "x": 1117.1610107421875, + "y": 170.177001953125 }, { - "x": 1096.696044921875, - "y": -44.72600173950195 + "x": 1123.470947265625, + "y": 188.2729949951172 }, { - "x": 1101.2449951171875, - "y": -26.108999252319336 + "x": 1128.02001953125, + "y": 206.88999938964844 }, { - "x": 1103.990966796875, - "y": -7.142000198364258 + "x": 1130.7659912109375, + "y": 225.85699462890625 }, { - "x": 1104.9100341796875, - "y": 12 + "x": 1131.6839599609375, + "y": 245 }, { - "x": 1103.990966796875, - "y": 31.142000198364258 + "x": 1130.7659912109375, + "y": 264.1419982910156 }, { - "x": 1101.2449951171875, - "y": 50.10900115966797 + "x": 1128.02001953125, + "y": 283.1090087890625 }, { - "x": 1096.696044921875, - "y": 68.72599792480469 + "x": 1123.470947265625, + "y": 301.72601318359375 }, { - "x": 1090.385986328125, - "y": 86.8219985961914 + "x": 1117.1610107421875, + "y": 319.8219909667969 }, { - "x": 1082.373046875, - "y": 104.23100280761719 + "x": 1109.14794921875, + "y": 337.2309875488281 }, { - "x": 1072.72998046875, - "y": 120.79399871826172 + "x": 1099.5050048828125, + "y": 353.79400634765625 }, { - "x": 1061.5469970703125, - "y": 136.35699462890625 + "x": 1088.321044921875, + "y": 369.35699462890625 }, { - "x": 1048.925048828125, - "y": 150.7779998779297 + "x": 1075.699951171875, + "y": 383.77801513671875 }, { - "x": 1034.98095703125, - "y": 163.9250030517578 + "x": 1061.7550048828125, + "y": 396.92498779296875 }, { - "x": 1019.8419799804688, - "y": 175.677001953125 + "x": 1046.616943359375, + "y": 408.677001953125 }, { - "x": 1003.6480102539062, - "y": 185.927001953125 + "x": 1030.4229736328125, + "y": 418.927001953125 }, { - "x": 986.5479736328125, - "y": 194.57899475097656 + "x": 1013.322021484375, + "y": 427.5790100097656 }, { - "x": 968.697998046875, - "y": 201.5540008544922 + "x": 995.4719848632812, + "y": 434.5539855957031 }, { - "x": 950.2620239257812, - "y": 206.7899932861328 + "x": 977.0360107421875, + "y": 439.7900085449219 }, { - "x": 931.4099731445312, - "y": 210.23599243164062 + "x": 958.1840209960938, + "y": 443.2359924316406 } ], "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 43b78e2ec4..df6d4310ef 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-2468300420 .fill-N1{fill:#0A0F25;} + .d2-2468300420 .fill-N7{fill:#FFFFFF;} + .d2-2468300420 .fill-B1{fill:#0D32B2;} + .d2-2468300420 .fill-B5{fill:#EDF0FD;} + .d2-2468300420 .stroke-B1{stroke:#0D32B2;} + .d2-2468300420 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>abcdabcab + \ No newline at end of file From 183f3522cc6e42ad3a7577bb737fa6c6ed2a0ee0 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Sun, 6 Sep 2026 13:19:27 +0900 Subject: [PATCH 11/17] Log the cycle layout as cycle, not sequence --- d2layouts/d2layouts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2layouts/d2layouts.go b/d2layouts/d2layouts.go index 98192c1504..0428224863 100644 --- a/d2layouts/d2layouts.go +++ b/d2layouts/d2layouts.go @@ -291,7 +291,7 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co return err } case CycleDiagram: - log.Debug(ctx, "layout sequence", slog.Any("rootlevel", g.RootLevel), slog.Any("shapes", g.PrintString())) + log.Debug(ctx, "layout cycle", slog.Any("rootlevel", g.RootLevel), slog.Any("shapes", g.PrintString())) err = d2cycle.Layout(ctx, g, coreLayout) if err != nil { return err From f1828a0e1728ca2a5234adcb3649bf3853c782c8 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Sun, 6 Sep 2026 13:54:22 +0900 Subject: [PATCH 12/17] Add the cycle shape to the changelog --- ci/release/changelogs/next.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 77f9b25012..c6c1f27b40 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,6 +1,7 @@ #### Features 🚀 - exports: render PNG, GIF, PDF, and PPTX with the built-in renderer +- shape: `cycle` lays a container's children out on a circle and routes their connections as arcs that start and stop on the shape borders #### Improvements 🧹 From 800a08bc5091077444c4706418bf533ad79c329e Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Tue, 8 Sep 2026 21:16:40 +0900 Subject: [PATCH 13/17] Add the tala goldens for the cycle diagram v0.9.0 vendored TALA into the repository, so the txtar suite now runs every case under dagre, elk and tala. The cycle-diagram case had no tala goldens and failed with a missing board. The layout is unchanged; these are the recorded output of the engine that ships with the tree. --- .../txtar/cycle-diagram/tala/board.exp.json | 1496 +++++++++++++++++ .../txtar/cycle-diagram/tala/sketch.exp.svg | 29 + 2 files changed, 1525 insertions(+) create mode 100644 e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg diff --git a/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json new file mode 100644 index 0000000000..7c238cdd54 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json @@ -0,0 +1,1496 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", + "shapes": [ + { + "id": "1", + "type": "cycle", + "pos": { + "x": 246, + "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": 447, + "y": 0 + }, + "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": "1.b", + "type": "rectangle", + "pos": { + "x": 647, + "y": 200 + }, + "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": "1.c", + "type": "rectangle", + "pos": { + "x": 447, + "y": 400 + }, + "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": "1.d", + "type": "rectangle", + "pos": { + "x": 246, + "y": 200 + }, + "width": 54, + "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": "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": 720, + "y": 0 + }, + "width": 399, + "height": 433, + "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": 893, + "y": 0 + }, + "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": 1066, + "y": 300 + }, + "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": 720, + "y": 300 + }, + "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": 0, + "y": 0 + }, + "width": 226, + "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": 0, + "y": 0 + }, + "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": 0, + "y": 400 + }, + "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": 500.31500244140625, + "y": 34.76300048828125 + }, + { + "x": 508.69500732421875, + "y": 36.06399917602539 + }, + { + "x": 517.0120239257812, + "y": 37.720001220703125 + }, + { + "x": 525.2509765625, + "y": 39.72700119018555 + }, + { + "x": 533.3980102539062, + "y": 42.08100128173828 + }, + { + "x": 541.43798828125, + "y": 44.77899932861328 + }, + { + "x": 549.3569946289062, + "y": 47.814998626708984 + }, + { + "x": 557.1389770507812, + "y": 51.18299865722656 + }, + { + "x": 564.77197265625, + "y": 54.87900161743164 + }, + { + "x": 572.2410278320312, + "y": 58.89500045776367 + }, + { + "x": 579.5330200195312, + "y": 63.2239990234375 + }, + { + "x": 586.635009765625, + "y": 67.85800170898438 + }, + { + "x": 593.5339965820312, + "y": 72.79000091552734 + }, + { + "x": 600.218017578125, + "y": 78.00900268554688 + }, + { + "x": 606.6749877929688, + "y": 83.50700378417969 + }, + { + "x": 612.8930053710938, + "y": 89.27300262451172 + }, + { + "x": 618.8610229492188, + "y": 95.2979965209961 + }, + { + "x": 624.5679931640625, + "y": 101.57099914550781 + }, + { + "x": 630.0040283203125, + "y": 108.08000183105469 + }, + { + "x": 635.1589965820312, + "y": 114.81300354003906 + }, + { + "x": 640.0239868164062, + "y": 121.75900268554688 + }, + { + "x": 644.5910034179688, + "y": 128.90499877929688 + }, + { + "x": 648.8499755859375, + "y": 136.23800659179688 + }, + { + "x": 652.7949829101562, + "y": 143.74600219726562 + }, + { + "x": 656.4169921875, + "y": 151.41299438476562 + }, + { + "x": 659.7119750976562, + "y": 159.2270050048828 + }, + { + "x": 662.6719970703125, + "y": 167.1739959716797 + }, + { + "x": 665.2930297851562, + "y": 175.24000549316406 + }, + { + "x": 667.5689697265625, + "y": 183.40899658203125 + }, + { + "x": 669.4970092773438, + "y": 191.66700744628906 + }, + { + "x": 671.073974609375, + "y": 200 + } + ], + "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": 671.073974609375, + "y": 266 + }, + { + "x": 669.4970092773438, + "y": 274.3320007324219 + }, + { + "x": 667.5689697265625, + "y": 282.5899963378906 + }, + { + "x": 665.2930297851562, + "y": 290.7590026855469 + }, + { + "x": 662.6719970703125, + "y": 298.82501220703125 + }, + { + "x": 659.7119750976562, + "y": 306.7720031738281 + }, + { + "x": 656.4169921875, + "y": 314.58599853515625 + }, + { + "x": 652.7949829101562, + "y": 322.25299072265625 + }, + { + "x": 648.8499755859375, + "y": 329.760986328125 + }, + { + "x": 644.5910034179688, + "y": 337.093994140625 + }, + { + "x": 640.0239868164062, + "y": 344.239990234375 + }, + { + "x": 635.1589965820312, + "y": 351.1860046386719 + }, + { + "x": 630.0040283203125, + "y": 357.91900634765625 + }, + { + "x": 624.5679931640625, + "y": 364.4280090332031 + }, + { + "x": 618.8610229492188, + "y": 370.70098876953125 + }, + { + "x": 612.8930053710938, + "y": 376.72601318359375 + }, + { + "x": 606.6749877929688, + "y": 382.49200439453125 + }, + { + "x": 600.218017578125, + "y": 387.989990234375 + }, + { + "x": 593.5339965820312, + "y": 393.2090148925781 + }, + { + "x": 586.635009765625, + "y": 398.1409912109375 + }, + { + "x": 579.5330200195312, + "y": 402.7749938964844 + }, + { + "x": 572.2410278320312, + "y": 407.10400390625 + }, + { + "x": 564.77197265625, + "y": 411.1199951171875 + }, + { + "x": 557.1389770507812, + "y": 414.8160095214844 + }, + { + "x": 549.3569946289062, + "y": 418.1839904785156 + }, + { + "x": 541.43798828125, + "y": 421.2200012207031 + }, + { + "x": 533.3980102539062, + "y": 423.9179992675781 + }, + { + "x": 525.2509765625, + "y": 426.2720031738281 + }, + { + "x": 517.0120239257812, + "y": 428.27899169921875 + }, + { + "x": 508.69500732421875, + "y": 429.93499755859375 + }, + { + "x": 500.31500244140625, + "y": 431.2359924316406 + } + ], + "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": 447.31500244140625, + "y": 431.2359924316406 + }, + { + "x": 438.93499755859375, + "y": 429.93499755859375 + }, + { + "x": 430.6180114746094, + "y": 428.27899169921875 + }, + { + "x": 422.37799072265625, + "y": 426.2720031738281 + }, + { + "x": 414.2309875488281, + "y": 423.9179992675781 + }, + { + "x": 406.1910095214844, + "y": 421.2200012207031 + }, + { + "x": 398.27301025390625, + "y": 418.1839904785156 + }, + { + "x": 390.4909973144531, + "y": 414.8160095214844 + }, + { + "x": 382.8580017089844, + "y": 411.1199951171875 + }, + { + "x": 375.3890075683594, + "y": 407.10400390625 + }, + { + "x": 368.09698486328125, + "y": 402.7749938964844 + }, + { + "x": 360.9949951171875, + "y": 398.1409912109375 + }, + { + "x": 354.0950012207031, + "y": 393.2090148925781 + }, + { + "x": 347.4110107421875, + "y": 387.989990234375 + }, + { + "x": 340.9540100097656, + "y": 382.49200439453125 + }, + { + "x": 334.73699951171875, + "y": 376.72601318359375 + }, + { + "x": 328.7690124511719, + "y": 370.70098876953125 + }, + { + "x": 323.06201171875, + "y": 364.4280090332031 + }, + { + "x": 317.6260070800781, + "y": 357.91900634765625 + }, + { + "x": 312.4700012207031, + "y": 351.1860046386719 + }, + { + "x": 307.6050109863281, + "y": 344.239990234375 + }, + { + "x": 303.03900146484375, + "y": 337.093994140625 + }, + { + "x": 298.7799987792969, + "y": 329.760986328125 + }, + { + "x": 294.8349914550781, + "y": 322.25299072265625 + }, + { + "x": 291.2120056152344, + "y": 314.58599853515625 + }, + { + "x": 287.9179992675781, + "y": 306.7720031738281 + }, + { + "x": 284.9580078125, + "y": 298.82501220703125 + }, + { + "x": 282.3370056152344, + "y": 290.7590026855469 + }, + { + "x": 280.0610046386719, + "y": 282.5899963378906 + }, + { + "x": 278.1319885253906, + "y": 274.3320007324219 + }, + { + "x": 276.5559997558594, + "y": 266 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "2.(a -> b)[0]", + "src": "2.a", + "srcArrow": "none", + "dst": "2.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": 946.52001953125, + "y": 34.76300048828125 + }, + { + "x": 958.2249755859375, + "y": 36.68299865722656 + }, + { + "x": 969.7960205078125, + "y": 39.292999267578125 + }, + { + "x": 981.1920166015625, + "y": 42.58399963378906 + }, + { + "x": 992.3729858398438, + "y": 46.54600143432617 + }, + { + "x": 1003.2990112304688, + "y": 51.16299819946289 + }, + { + "x": 1013.9320068359375, + "y": 56.41899871826172 + }, + { + "x": 1024.2349853515625, + "y": 62.297000885009766 + }, + { + "x": 1034.1710205078125, + "y": 68.7760009765625 + }, + { + "x": 1043.7060546875, + "y": 75.83200073242188 + }, + { + "x": 1052.8060302734375, + "y": 83.44000244140625 + }, + { + "x": 1061.43798828125, + "y": 91.57499694824219 + }, + { + "x": 1069.573974609375, + "y": 100.20800018310547 + }, + { + "x": 1077.1829833984375, + "y": 109.30699920654297 + }, + { + "x": 1084.239013671875, + "y": 118.84200286865234 + }, + { + "x": 1090.718017578125, + "y": 128.7779998779297 + }, + { + "x": 1096.595947265625, + "y": 139.0800018310547 + }, + { + "x": 1101.85302734375, + "y": 149.71299743652344 + }, + { + "x": 1106.470947265625, + "y": 160.63900756835938 + }, + { + "x": 1110.4329833984375, + "y": 171.82000732421875 + }, + { + "x": 1113.7249755859375, + "y": 183.21499633789062 + }, + { + "x": 1116.3349609375, + "y": 194.78599548339844 + }, + { + "x": 1118.2550048828125, + "y": 206.49200439453125 + }, + { + "x": 1119.47802734375, + "y": 218.2899932861328 + }, + { + "x": 1119.9990234375, + "y": 230.13999938964844 + }, + { + "x": 1119.8170166015625, + "y": 242 + }, + { + "x": 1118.9320068359375, + "y": 253.82899475097656 + }, + { + "x": 1117.3480224609375, + "y": 265.5840148925781 + }, + { + "x": 1115.0689697265625, + "y": 277.2250061035156 + }, + { + "x": 1112.10400390625, + "y": 288.7099914550781 + }, + { + "x": 1108.4639892578125, + "y": 300 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "2.(b -> c)[0]", + "src": "2.b", + "srcArrow": "none", + "dst": "2.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": 1069.3890380859375, + "y": 366 + }, + { + "x": 1061.678955078125, + "y": 374.1820068359375 + }, + { + "x": 1053.52197265625, + "y": 381.91900634765625 + }, + { + "x": 1044.9429931640625, + "y": 389.1860046386719 + }, + { + "x": 1035.969970703125, + "y": 395.9580078125 + }, + { + "x": 1026.6300048828125, + "y": 402.21600341796875 + }, + { + "x": 1016.9530029296875, + "y": 407.9389953613281 + }, + { + "x": 1006.969970703125, + "y": 413.1099853515625 + }, + { + "x": 996.7119750976562, + "y": 417.71099853515625 + }, + { + "x": 986.2119750976562, + "y": 421.7279968261719 + }, + { + "x": 975.5029907226562, + "y": 425.1499938964844 + }, + { + "x": 964.6179809570312, + "y": 427.9639892578125 + }, + { + "x": 953.5919799804688, + "y": 430.1619873046875 + }, + { + "x": 942.4609985351562, + "y": 431.73699951171875 + }, + { + "x": 931.2579956054688, + "y": 432.6839904785156 + }, + { + "x": 920.02001953125, + "y": 433 + }, + { + "x": 908.781982421875, + "y": 432.6839904785156 + }, + { + "x": 897.5789794921875, + "y": 431.73699951171875 + }, + { + "x": 886.447998046875, + "y": 430.1619873046875 + }, + { + "x": 875.4219970703125, + "y": 427.9639892578125 + }, + { + "x": 864.5369873046875, + "y": 425.1499938964844 + }, + { + "x": 853.8280029296875, + "y": 421.7279968261719 + }, + { + "x": 843.3280029296875, + "y": 417.71099853515625 + }, + { + "x": 833.0700073242188, + "y": 413.1099853515625 + }, + { + "x": 823.0869750976562, + "y": 407.9389953613281 + }, + { + "x": 813.4099731445312, + "y": 402.21600341796875 + }, + { + "x": 804.0700073242188, + "y": 395.9580078125 + }, + { + "x": 795.0960083007812, + "y": 389.1860046386719 + }, + { + "x": 786.5180053710938, + "y": 381.91900634765625 + }, + { + "x": 778.3610229492188, + "y": 374.1820068359375 + }, + { + "x": 770.6510009765625, + "y": 366 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "3.(a -> b)[0]", + "src": "3.a", + "srcArrow": "none", + "dst": "3.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": 53, + "y": 34.76300048828125 + }, + { + "x": 71.85199737548828, + "y": 38.20899963378906 + }, + { + "x": 90.28800201416016, + "y": 43.44499969482422 + }, + { + "x": 108.13800048828125, + "y": 50.41999816894531 + }, + { + "x": 125.23799896240234, + "y": 59.071998596191406 + }, + { + "x": 141.4320068359375, + "y": 69.3219985961914 + }, + { + "x": 156.5709991455078, + "y": 81.0739974975586 + }, + { + "x": 170.51499938964844, + "y": 94.22100067138672 + }, + { + "x": 183.13699340820312, + "y": 108.64199829101562 + }, + { + "x": 194.32000732421875, + "y": 124.20500183105469 + }, + { + "x": 203.96299743652344, + "y": 140.76800537109375 + }, + { + "x": 211.9759979248047, + "y": 158.177001953125 + }, + { + "x": 218.28599548339844, + "y": 176.2729949951172 + }, + { + "x": 222.8350067138672, + "y": 194.88999938964844 + }, + { + "x": 225.58099365234375, + "y": 213.85699462890625 + }, + { + "x": 226.5, + "y": 233 + }, + { + "x": 225.58099365234375, + "y": 252.14199829101562 + }, + { + "x": 222.8350067138672, + "y": 271.1090087890625 + }, + { + "x": 218.28599548339844, + "y": 289.72601318359375 + }, + { + "x": 211.9759979248047, + "y": 307.8219909667969 + }, + { + "x": 203.96299743652344, + "y": 325.2309875488281 + }, + { + "x": 194.32000732421875, + "y": 341.79400634765625 + }, + { + "x": 183.13699340820312, + "y": 357.35699462890625 + }, + { + "x": 170.51499938964844, + "y": 371.77801513671875 + }, + { + "x": 156.5709991455078, + "y": 384.92498779296875 + }, + { + "x": 141.4320068359375, + "y": 396.677001953125 + }, + { + "x": 125.23799896240234, + "y": 406.927001953125 + }, + { + "x": 108.13800048828125, + "y": 415.5790100097656 + }, + { + "x": 90.28800201416016, + "y": 422.5539855957031 + }, + { + "x": 71.85199737548828, + "y": 427.7900085449219 + }, + { + "x": 53, + "y": 431.2359924316406 + } + ], + "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/tala/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg new file mode 100644 index 0000000000..5f5ec34863 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg @@ -0,0 +1,29 @@ +abcdabcab + + + \ No newline at end of file From b8f6ea609f8c77ca7b4f7bd04c3cf78789e59133 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Tue, 8 Sep 2026 21:43:13 +0900 Subject: [PATCH 14/17] Round the cycle board up to whole pixels The exporter truncates positions and sizes to integers, and a circle leaves shapes on half pixels, so the container could be written one pixel narrower than the child sitting on its right edge and its border was drawn through that child. It showed up only under tala, where the board lands on a fractional offset: container 1 ended at 699 while b ended at 700. Sweeping all 162 txtar goldens, that was the only case in the suite of a child outside its container that is not a sequence diagram lifeline. Rounding the board size up makes the box contain its children under all three engines, and the three now agree. --- d2layouts/d2cycle/layout.go | 7 +- .../txtar/cycle-diagram/dagre/board.exp.json | 206 ++++----- .../txtar/cycle-diagram/dagre/sketch.exp.svg | 22 +- .../txtar/cycle-diagram/elk/board.exp.json | 200 ++++----- .../txtar/cycle-diagram/elk/sketch.exp.svg | 22 +- .../txtar/cycle-diagram/tala/board.exp.json | 402 +++++++++--------- .../txtar/cycle-diagram/tala/sketch.exp.svg | 22 +- 7 files changed, 442 insertions(+), 439 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index 5dc1386f70..d04075a704 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -83,8 +83,11 @@ func normalizeToOrigin(g *d2graph.Graph) { edge.Move(dx, dy) } } - g.Root.Width = maxX - minX - g.Root.Height = maxY - minY + // The exporter truncates positions and sizes to whole pixels, and the + // circle leaves shapes on half pixels, so a bare width can round down + // past a child and draw the container border through it. Round up. + g.Root.Width = math.Ceil(maxX - minX) + g.Root.Height = math.Ceil(maxY - minY) } func calculateRadius(objects []*d2graph.Object) float64 { diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json index bd2e258286..ba65555f70 100644 --- a/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/board.exp.json @@ -19,7 +19,7 @@ "x": 0, "y": 0 }, - "width": 453, + "width": 454, "height": 466, "opacity": 1, "strokeDash": 0, @@ -225,10 +225,10 @@ "id": "2", "type": "cycle", "pos": { - "x": 513, + "x": 514, "y": 17 }, - "width": 399, + "width": 400, "height": 433, "opacity": 1, "strokeDash": 0, @@ -266,7 +266,7 @@ "id": "2.a", "type": "rectangle", "pos": { - "x": 686, + "x": 687, "y": 17 }, "width": 53, @@ -308,7 +308,7 @@ "id": "2.b", "type": "rectangle", "pos": { - "x": 859, + "x": 860, "y": 317 }, "width": 53, @@ -350,7 +350,7 @@ "id": "2.c", "type": "rectangle", "pos": { - "x": 513, + "x": 514, "y": 317 }, "width": 53, @@ -392,10 +392,10 @@ "id": "3", "type": "cycle", "pos": { - "x": 972, + "x": 974, "y": 0 }, - "width": 226, + "width": 227, "height": 466, "opacity": 1, "strokeDash": 0, @@ -433,7 +433,7 @@ "id": "3.a", "type": "rectangle", "pos": { - "x": 972, + "x": 974, "y": 0 }, "width": 53, @@ -475,7 +475,7 @@ "id": "3.b", "type": "rectangle", "pos": { - "x": 972, + "x": 974, "y": 400 }, "width": 53, @@ -1009,127 +1009,127 @@ "link": "", "route": [ { - "x": 739.2050170898438, + "x": 740.2050170898438, "y": 51.76300048828125 }, { - "x": 750.9099731445312, + "x": 751.9099731445312, "y": 53.68299865722656 }, { - "x": 762.4810180664062, + "x": 763.4810180664062, "y": 56.292999267578125 }, { - "x": 773.8770141601562, + "x": 774.8770141601562, "y": 59.58399963378906 }, { - "x": 785.0570068359375, + "x": 786.0570068359375, "y": 63.54600143432617 }, { - "x": 795.9829711914062, + "x": 796.9829711914062, "y": 68.16300201416016 }, { - "x": 806.6170043945312, + "x": 807.6170043945312, "y": 73.41899871826172 }, { - "x": 816.9199829101562, + "x": 817.9199829101562, "y": 79.2969970703125 }, { - "x": 826.8560180664062, + "x": 827.8560180664062, "y": 85.7760009765625 }, { - "x": 836.3909912109375, + "x": 837.3909912109375, "y": 92.83200073242188 }, { - "x": 845.489990234375, + "x": 846.489990234375, "y": 100.44000244140625 }, { - "x": 854.1229858398438, + "x": 855.1229858398438, "y": 108.57499694824219 }, { - "x": 862.2579956054688, + "x": 863.2579956054688, "y": 117.20800018310547 }, { - "x": 869.8670043945312, + "x": 870.8670043945312, "y": 126.30699920654297 }, { - "x": 876.9240112304688, + "x": 877.9240112304688, "y": 135.8419952392578 }, { - "x": 883.4030151367188, + "x": 884.4030151367188, "y": 145.7779998779297 }, { - "x": 889.281005859375, + "x": 890.281005859375, "y": 156.0800018310547 }, { - "x": 894.5380249023438, + "x": 895.5380249023438, "y": 166.71299743652344 }, { - "x": 899.156005859375, + "x": 900.156005859375, "y": 177.63900756835938 }, { - "x": 903.1170043945312, + "x": 904.1170043945312, "y": 188.82000732421875 }, { - "x": 906.4089965820312, + "x": 907.4089965820312, "y": 200.21499633789062 }, { - "x": 909.02001953125, + "x": 910.02001953125, "y": 211.78599548339844 }, { - "x": 910.9400024414062, + "x": 911.9400024414062, "y": 223.49200439453125 }, { - "x": 912.1630249023438, + "x": 913.1630249023438, "y": 235.2899932861328 }, { - "x": 912.6840209960938, + "x": 913.6840209960938, "y": 247.13999938964844 }, { - "x": 912.5020141601562, + "x": 913.5020141601562, "y": 259 }, { - "x": 911.6170043945312, + "x": 912.6170043945312, "y": 270.8290100097656 }, { - "x": 910.031982421875, + "x": 911.031982421875, "y": 282.5840148925781 }, { - "x": 907.7540283203125, + "x": 908.7540283203125, "y": 294.2250061035156 }, { - "x": 904.7890014648438, + "x": 905.7890014648438, "y": 305.7099914550781 }, { - "x": 901.1480102539062, + "x": 902.1480102539062, "y": 317 } ], @@ -1165,127 +1165,127 @@ "link": "", "route": [ { - "x": 862.072998046875, + "x": 863.072998046875, "y": 383 }, { - "x": 854.364013671875, + "x": 855.364013671875, "y": 391.1820068359375 }, { - "x": 846.2069702148438, + "x": 847.2069702148438, "y": 398.91900634765625 }, { - "x": 837.6279907226562, + "x": 838.6279907226562, "y": 406.1860046386719 }, { - "x": 828.655029296875, + "x": 829.655029296875, "y": 412.9580078125 }, { - "x": 819.3150024414062, + "x": 820.3150024414062, "y": 419.21600341796875 }, { - "x": 809.6380004882812, + "x": 810.6380004882812, "y": 424.9389953613281 }, { - "x": 799.655029296875, + "x": 800.655029296875, "y": 430.1099853515625 }, { - "x": 789.39697265625, + "x": 790.39697265625, "y": 434.71099853515625 }, { - "x": 778.89697265625, + "x": 779.89697265625, "y": 438.7279968261719 }, { - "x": 768.18701171875, + "x": 769.18701171875, "y": 442.1499938964844 }, { - "x": 757.302978515625, + "x": 758.302978515625, "y": 444.9639892578125 }, { - "x": 746.2769775390625, + "x": 747.2769775390625, "y": 447.1619873046875 }, { - "x": 735.14501953125, + "x": 736.14501953125, "y": 448.73699951171875 }, { - "x": 723.9429931640625, + "x": 724.9429931640625, "y": 449.6839904785156 }, { - "x": 712.7050170898438, + "x": 713.7050170898438, "y": 450 }, { - "x": 701.4660034179688, + "x": 702.4660034179688, "y": 449.6839904785156 }, { - "x": 690.2639770507812, + "x": 691.2639770507812, "y": 448.73699951171875 }, { - "x": 679.1320190429688, + "x": 680.1320190429688, "y": 447.1619873046875 }, { - "x": 668.1069946289062, + "x": 669.1069946289062, "y": 444.9639892578125 }, { - "x": 657.2219848632812, + "x": 658.2219848632812, "y": 442.1499938964844 }, { - "x": 646.5130004882812, + "x": 647.5130004882812, "y": 438.7279968261719 }, { - "x": 636.0120239257812, + "x": 637.0120239257812, "y": 434.71099853515625 }, { - "x": 625.7540283203125, + "x": 626.7540283203125, "y": 430.1099853515625 }, { - "x": 615.77099609375, + "x": 616.77099609375, "y": 424.9389953613281 }, { - "x": 606.094970703125, + "x": 607.094970703125, "y": 419.21600341796875 }, { - "x": 596.7550048828125, + "x": 597.7550048828125, "y": 412.9580078125 }, { - "x": 587.781005859375, + "x": 588.781005859375, "y": 406.1860046386719 }, { - "x": 579.2020263671875, + "x": 580.2020263671875, "y": 398.91900634765625 }, { - "x": 571.0449829101562, + "x": 572.0449829101562, "y": 391.1820068359375 }, { - "x": 563.3359985351562, + "x": 564.3359985351562, "y": 383 } ], @@ -1321,127 +1321,127 @@ "link": "", "route": [ { - "x": 1025, + "x": 1027, "y": 34.76300048828125 }, { - "x": 1043.85205078125, + "x": 1045.85205078125, "y": 38.20899963378906 }, { - "x": 1062.2879638671875, + "x": 1064.2879638671875, "y": 43.44499969482422 }, { - "x": 1080.137939453125, + "x": 1082.137939453125, "y": 50.41999816894531 }, { - "x": 1097.238037109375, + "x": 1099.238037109375, "y": 59.071998596191406 }, { - "x": 1113.4320068359375, + "x": 1115.4320068359375, "y": 69.3219985961914 }, { - "x": 1128.571044921875, + "x": 1130.571044921875, "y": 81.0739974975586 }, { - "x": 1142.5150146484375, + "x": 1144.5150146484375, "y": 94.22100067138672 }, { - "x": 1155.136962890625, + "x": 1157.136962890625, "y": 108.64199829101562 }, { - "x": 1166.3199462890625, + "x": 1168.3199462890625, "y": 124.20500183105469 }, { - "x": 1175.9630126953125, + "x": 1177.9630126953125, "y": 140.76800537109375 }, { - "x": 1183.9759521484375, + "x": 1185.9759521484375, "y": 158.177001953125 }, { - "x": 1190.2860107421875, + "x": 1192.2860107421875, "y": 176.2729949951172 }, { - "x": 1194.8349609375, + "x": 1196.8349609375, "y": 194.88999938964844 }, { - "x": 1197.5810546875, + "x": 1199.5810546875, "y": 213.85699462890625 }, { - "x": 1198.5, + "x": 1200.5, "y": 233 }, { - "x": 1197.5810546875, + "x": 1199.5810546875, "y": 252.14199829101562 }, { - "x": 1194.8349609375, + "x": 1196.8349609375, "y": 271.1090087890625 }, { - "x": 1190.2860107421875, + "x": 1192.2860107421875, "y": 289.72601318359375 }, { - "x": 1183.9759521484375, + "x": 1185.9759521484375, "y": 307.8219909667969 }, { - "x": 1175.9630126953125, + "x": 1177.9630126953125, "y": 325.2309875488281 }, { - "x": 1166.3199462890625, + "x": 1168.3199462890625, "y": 341.79400634765625 }, { - "x": 1155.136962890625, + "x": 1157.136962890625, "y": 357.35699462890625 }, { - "x": 1142.5150146484375, + "x": 1144.5150146484375, "y": 371.77801513671875 }, { - "x": 1128.571044921875, + "x": 1130.571044921875, "y": 384.92498779296875 }, { - "x": 1113.4320068359375, + "x": 1115.4320068359375, "y": 396.677001953125 }, { - "x": 1097.238037109375, + "x": 1099.238037109375, "y": 406.927001953125 }, { - "x": 1080.137939453125, + "x": 1082.137939453125, "y": 415.5790100097656 }, { - "x": 1062.2879638671875, + "x": 1064.2879638671875, "y": 422.5539855957031 }, { - "x": 1043.85205078125, + "x": 1045.85205078125, "y": 427.7900085449219 }, { - "x": 1025, + "x": 1027, "y": 431.2359924316406 } ], diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/sketch.exp.svg index 4dd629edfa..40257d2d40 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-1382812156 .fill-N1{fill:#0A0F25;} + .d2-1382812156 .fill-N7{fill:#FFFFFF;} + .d2-1382812156 .fill-B1{fill:#0D32B2;} + .d2-1382812156 .fill-B5{fill:#EDF0FD;} + .d2-1382812156 .stroke-B1{stroke:#0D32B2;} + .d2-1382812156 .stroke-B2{stroke:#0D32B2;}.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;}.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 dfd05143bb..720b49c4ec 100644 --- a/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/elk/board.exp.json @@ -225,7 +225,7 @@ "id": "2", "type": "cycle", "pos": { - "x": 485, + "x": 486, "y": 28 }, "width": 400, @@ -266,7 +266,7 @@ "id": "2.a", "type": "rectangle", "pos": { - "x": 658, + "x": 659, "y": 28 }, "width": 53, @@ -308,7 +308,7 @@ "id": "2.b", "type": "rectangle", "pos": { - "x": 831, + "x": 832, "y": 328 }, "width": 53, @@ -350,7 +350,7 @@ "id": "2.c", "type": "rectangle", "pos": { - "x": 485, + "x": 486, "y": 328 }, "width": 53, @@ -392,7 +392,7 @@ "id": "3", "type": "cycle", "pos": { - "x": 905, + "x": 906, "y": 12 }, "width": 227, @@ -433,7 +433,7 @@ "id": "3.a", "type": "rectangle", "pos": { - "x": 905, + "x": 906, "y": 12 }, "width": 53, @@ -475,7 +475,7 @@ "id": "3.b", "type": "rectangle", "pos": { - "x": 905, + "x": 906, "y": 412 }, "width": 53, @@ -1009,127 +1009,127 @@ "link": "", "route": [ { - "x": 711.7050170898438, + "x": 712.2050170898438, "y": 63.26300048828125 }, { - "x": 723.4099731445312, + "x": 723.9099731445312, "y": 65.18299865722656 }, { - "x": 734.9810180664062, + "x": 735.4810180664062, "y": 67.79299926757812 }, { - "x": 746.3770141601562, + "x": 746.8770141601562, "y": 71.08399963378906 }, { - "x": 757.5570068359375, + "x": 758.0570068359375, "y": 75.0459976196289 }, { - "x": 768.4829711914062, + "x": 768.9829711914062, "y": 79.66300201416016 }, { - "x": 779.1170043945312, + "x": 779.6170043945312, "y": 84.91899871826172 }, { - "x": 789.4199829101562, + "x": 789.9199829101562, "y": 90.7969970703125 }, { - "x": 799.3560180664062, + "x": 799.8560180664062, "y": 97.2760009765625 }, { - "x": 808.8909912109375, + "x": 809.3909912109375, "y": 104.33200073242188 }, { - "x": 817.989990234375, + "x": 818.489990234375, "y": 111.94000244140625 }, { - "x": 826.6229858398438, + "x": 827.1229858398438, "y": 120.07499694824219 }, { - "x": 834.7579956054688, + "x": 835.2579956054688, "y": 128.70799255371094 }, { - "x": 842.3670043945312, + "x": 842.8670043945312, "y": 137.8070068359375 }, { - "x": 849.4240112304688, + "x": 849.9240112304688, "y": 147.3419952392578 }, { - "x": 855.9030151367188, + "x": 856.4030151367188, "y": 157.2779998779297 }, { - "x": 861.781005859375, + "x": 862.281005859375, "y": 167.5800018310547 }, { - "x": 867.0380249023438, + "x": 867.5380249023438, "y": 178.21299743652344 }, { - "x": 871.656005859375, + "x": 872.156005859375, "y": 189.13900756835938 }, { - "x": 875.6170043945312, + "x": 876.1170043945312, "y": 200.32000732421875 }, { - "x": 878.9089965820312, + "x": 879.4089965820312, "y": 211.71499633789062 }, { - "x": 881.52001953125, + "x": 882.02001953125, "y": 223.28599548339844 }, { - "x": 883.4400024414062, + "x": 883.9400024414062, "y": 234.99200439453125 }, { - "x": 884.6630249023438, + "x": 885.1630249023438, "y": 246.7899932861328 }, { - "x": 885.1840209960938, + "x": 885.6840209960938, "y": 258.6400146484375 }, { - "x": 885.0020141601562, + "x": 885.5020141601562, "y": 270.5 }, { - "x": 884.1170043945312, + "x": 884.6170043945312, "y": 282.3290100097656 }, { - "x": 882.531982421875, + "x": 883.031982421875, "y": 294.0840148925781 }, { - "x": 880.2540283203125, + "x": 880.7540283203125, "y": 305.7250061035156 }, { - "x": 877.2890014648438, + "x": 877.7890014648438, "y": 317.2099914550781 }, { - "x": 873.6480102539062, + "x": 874.1480102539062, "y": 328.5 } ], @@ -1165,127 +1165,127 @@ "link": "", "route": [ { - "x": 834.572998046875, + "x": 835.072998046875, "y": 394.5 }, { - "x": 826.864013671875, + "x": 827.364013671875, "y": 402.6820068359375 }, { - "x": 818.7069702148438, + "x": 819.2069702148438, "y": 410.41900634765625 }, { - "x": 810.1279907226562, + "x": 810.6279907226562, "y": 417.6860046386719 }, { - "x": 801.155029296875, + "x": 801.655029296875, "y": 424.4580078125 }, { - "x": 791.8150024414062, + "x": 792.3150024414062, "y": 430.71600341796875 }, { - "x": 782.1380004882812, + "x": 782.6380004882812, "y": 436.4389953613281 }, { - "x": 772.155029296875, + "x": 772.655029296875, "y": 441.6099853515625 }, { - "x": 761.89697265625, + "x": 762.39697265625, "y": 446.21099853515625 }, { - "x": 751.39697265625, + "x": 751.89697265625, "y": 450.2279968261719 }, { - "x": 740.68701171875, + "x": 741.18701171875, "y": 453.6499938964844 }, { - "x": 729.802978515625, + "x": 730.302978515625, "y": 456.4639892578125 }, { - "x": 718.7769775390625, + "x": 719.2769775390625, "y": 458.6619873046875 }, { - "x": 707.64501953125, + "x": 708.14501953125, "y": 460.23699951171875 }, { - "x": 696.4429931640625, + "x": 696.9429931640625, "y": 461.1839904785156 }, { - "x": 685.2050170898438, + "x": 685.7050170898438, "y": 461.5 }, { - "x": 673.9660034179688, + "x": 674.4660034179688, "y": 461.1839904785156 }, { - "x": 662.7639770507812, + "x": 663.2639770507812, "y": 460.23699951171875 }, { - "x": 651.6320190429688, + "x": 652.1320190429688, "y": 458.6619873046875 }, { - "x": 640.6069946289062, + "x": 641.1069946289062, "y": 456.4639892578125 }, { - "x": 629.7219848632812, + "x": 630.2219848632812, "y": 453.6499938964844 }, { - "x": 619.0130004882812, + "x": 619.5130004882812, "y": 450.2279968261719 }, { - "x": 608.5120239257812, + "x": 609.0120239257812, "y": 446.21099853515625 }, { - "x": 598.2540283203125, + "x": 598.7540283203125, "y": 441.6099853515625 }, { - "x": 588.27099609375, + "x": 588.77099609375, "y": 436.4389953613281 }, { - "x": 578.594970703125, + "x": 579.094970703125, "y": 430.71600341796875 }, { - "x": 569.2550048828125, + "x": 569.7550048828125, "y": 424.4580078125 }, { - "x": 560.281005859375, + "x": 560.781005859375, "y": 417.6860046386719 }, { - "x": 551.7020263671875, + "x": 552.2020263671875, "y": 410.41900634765625 }, { - "x": 543.5449829101562, + "x": 544.0449829101562, "y": 402.6820068359375 }, { - "x": 535.8359985351562, + "x": 536.3359985351562, "y": 394.5 } ], @@ -1321,127 +1321,127 @@ "link": "", "route": [ { - "x": 958.1840209960938, + "x": 959, "y": 46.76300048828125 }, { - "x": 977.0360107421875, + "x": 977.8519897460938, "y": 50.20899963378906 }, { - "x": 995.4719848632812, + "x": 996.2880249023438, "y": 55.44499969482422 }, { - "x": 1013.322021484375, + "x": 1014.1380004882812, "y": 62.41999816894531 }, { - "x": 1030.4229736328125, + "x": 1031.238037109375, "y": 71.0719985961914 }, { - "x": 1046.616943359375, + "x": 1047.4320068359375, "y": 81.3219985961914 }, { - "x": 1061.7550048828125, + "x": 1062.571044921875, "y": 93.0739974975586 }, { - "x": 1075.699951171875, + "x": 1076.5150146484375, "y": 106.22100067138672 }, { - "x": 1088.321044921875, + "x": 1089.136962890625, "y": 120.64199829101562 }, { - "x": 1099.5050048828125, + "x": 1100.3199462890625, "y": 136.2050018310547 }, { - "x": 1109.14794921875, + "x": 1109.9630126953125, "y": 152.76800537109375 }, { - "x": 1117.1610107421875, + "x": 1117.9759521484375, "y": 170.177001953125 }, { - "x": 1123.470947265625, + "x": 1124.2860107421875, "y": 188.2729949951172 }, { - "x": 1128.02001953125, + "x": 1128.8349609375, "y": 206.88999938964844 }, { - "x": 1130.7659912109375, + "x": 1131.5810546875, "y": 225.85699462890625 }, { - "x": 1131.6839599609375, + "x": 1132.5, "y": 245 }, { - "x": 1130.7659912109375, + "x": 1131.5810546875, "y": 264.1419982910156 }, { - "x": 1128.02001953125, + "x": 1128.8349609375, "y": 283.1090087890625 }, { - "x": 1123.470947265625, + "x": 1124.2860107421875, "y": 301.72601318359375 }, { - "x": 1117.1610107421875, + "x": 1117.9759521484375, "y": 319.8219909667969 }, { - "x": 1109.14794921875, + "x": 1109.9630126953125, "y": 337.2309875488281 }, { - "x": 1099.5050048828125, + "x": 1100.3199462890625, "y": 353.79400634765625 }, { - "x": 1088.321044921875, + "x": 1089.136962890625, "y": 369.35699462890625 }, { - "x": 1075.699951171875, + "x": 1076.5150146484375, "y": 383.77801513671875 }, { - "x": 1061.7550048828125, + "x": 1062.571044921875, "y": 396.92498779296875 }, { - "x": 1046.616943359375, + "x": 1047.4320068359375, "y": 408.677001953125 }, { - "x": 1030.4229736328125, + "x": 1031.238037109375, "y": 418.927001953125 }, { - "x": 1013.322021484375, + "x": 1014.1380004882812, "y": 427.5790100097656 }, { - "x": 995.4719848632812, + "x": 996.2880249023438, "y": 434.5539855957031 }, { - "x": 977.0360107421875, + "x": 977.8519897460938, "y": 439.7900085449219 }, { - "x": 958.1840209960938, + "x": 959, "y": 443.2359924316406 } ], diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/sketch.exp.svg index df6d4310ef..a25815cecc 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-2726488059 .fill-N1{fill:#0A0F25;} + .d2-2726488059 .fill-N7{fill:#FFFFFF;} + .d2-2726488059 .fill-B1{fill:#0D32B2;} + .d2-2726488059 .fill-B5{fill:#EDF0FD;} + .d2-2726488059 .stroke-B1{stroke:#0D32B2;} + .d2-2726488059 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>abcdabcab + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json b/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json index 7c238cdd54..befb9fd6d0 100644 --- a/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json +++ b/e2etests/testdata/txtar/cycle-diagram/tala/board.exp.json @@ -16,10 +16,10 @@ "id": "1", "type": "cycle", "pos": { - "x": 246, + "x": 0, "y": 0 }, - "width": 453, + "width": 454, "height": 466, "opacity": 1, "strokeDash": 0, @@ -57,7 +57,7 @@ "id": "1.a", "type": "rectangle", "pos": { - "x": 447, + "x": 200, "y": 0 }, "width": 53, @@ -99,7 +99,7 @@ "id": "1.b", "type": "rectangle", "pos": { - "x": 647, + "x": 400, "y": 200 }, "width": 53, @@ -141,7 +141,7 @@ "id": "1.c", "type": "rectangle", "pos": { - "x": 447, + "x": 200, "y": 400 }, "width": 53, @@ -183,7 +183,7 @@ "id": "1.d", "type": "rectangle", "pos": { - "x": 246, + "x": 0, "y": 200 }, "width": 54, @@ -225,10 +225,10 @@ "id": "2", "type": "cycle", "pos": { - "x": 720, + "x": 474, "y": 0 }, - "width": 399, + "width": 400, "height": 433, "opacity": 1, "strokeDash": 0, @@ -266,7 +266,7 @@ "id": "2.a", "type": "rectangle", "pos": { - "x": 893, + "x": 647, "y": 0 }, "width": 53, @@ -308,7 +308,7 @@ "id": "2.b", "type": "rectangle", "pos": { - "x": 1066, + "x": 820, "y": 300 }, "width": 53, @@ -350,7 +350,7 @@ "id": "2.c", "type": "rectangle", "pos": { - "x": 720, + "x": 474, "y": 300 }, "width": 53, @@ -392,10 +392,10 @@ "id": "3", "type": "cycle", "pos": { - "x": 0, + "x": 894, "y": 0 }, - "width": 226, + "width": 227, "height": 466, "opacity": 1, "strokeDash": 0, @@ -433,7 +433,7 @@ "id": "3.a", "type": "rectangle", "pos": { - "x": 0, + "x": 894, "y": 0 }, "width": 53, @@ -475,7 +475,7 @@ "id": "3.b", "type": "rectangle", "pos": { - "x": 0, + "x": 894, "y": 400 }, "width": 53, @@ -541,127 +541,127 @@ "link": "", "route": [ { - "x": 500.31500244140625, + "x": 253.5, "y": 34.76300048828125 }, { - "x": 508.69500732421875, + "x": 261.8789978027344, "y": 36.06399917602539 }, { - "x": 517.0120239257812, + "x": 270.1969909667969, "y": 37.720001220703125 }, { - "x": 525.2509765625, + "x": 278.4360046386719, "y": 39.72700119018555 }, { - "x": 533.3980102539062, + "x": 286.5830078125, "y": 42.08100128173828 }, { - "x": 541.43798828125, + "x": 294.62298583984375, "y": 44.77899932861328 }, { - "x": 549.3569946289062, + "x": 302.5409851074219, "y": 47.814998626708984 }, { - "x": 557.1389770507812, + "x": 310.3240051269531, "y": 51.18299865722656 }, { - "x": 564.77197265625, + "x": 317.95599365234375, "y": 54.87900161743164 }, { - "x": 572.2410278320312, + "x": 325.4259948730469, "y": 58.89500045776367 }, { - "x": 579.5330200195312, + "x": 332.7179870605469, "y": 63.2239990234375 }, { - "x": 586.635009765625, + "x": 339.82000732421875, "y": 67.85800170898438 }, { - "x": 593.5339965820312, + "x": 346.718994140625, "y": 72.79000091552734 }, { - "x": 600.218017578125, + "x": 353.40301513671875, "y": 78.00900268554688 }, { - "x": 606.6749877929688, + "x": 359.8599853515625, "y": 83.50700378417969 }, { - "x": 612.8930053710938, + "x": 366.0780029296875, "y": 89.27300262451172 }, { - "x": 618.8610229492188, + "x": 372.0459899902344, "y": 95.2979965209961 }, { - "x": 624.5679931640625, + "x": 377.75299072265625, "y": 101.57099914550781 }, { - "x": 630.0040283203125, + "x": 383.1889953613281, "y": 108.08000183105469 }, { - "x": 635.1589965820312, + "x": 388.343994140625, "y": 114.81300354003906 }, { - "x": 640.0239868164062, + "x": 393.2090148925781, "y": 121.75900268554688 }, { - "x": 644.5910034179688, + "x": 397.7749938964844, "y": 128.90499877929688 }, { - "x": 648.8499755859375, + "x": 402.0350036621094, "y": 136.23800659179688 }, { - "x": 652.7949829101562, + "x": 405.97900390625, "y": 143.74600219726562 }, { - "x": 656.4169921875, + "x": 409.60198974609375, "y": 151.41299438476562 }, { - "x": 659.7119750976562, + "x": 412.89599609375, "y": 159.2270050048828 }, { - "x": 662.6719970703125, + "x": 415.85699462890625, "y": 167.1739959716797 }, { - "x": 665.2930297851562, + "x": 418.47698974609375, "y": 175.24000549316406 }, { - "x": 667.5689697265625, + "x": 420.7539978027344, "y": 183.40899658203125 }, { - "x": 669.4970092773438, + "x": 422.6820068359375, "y": 191.66700744628906 }, { - "x": 671.073974609375, + "x": 424.25799560546875, "y": 200 } ], @@ -697,127 +697,127 @@ "link": "", "route": [ { - "x": 671.073974609375, + "x": 424.25799560546875, "y": 266 }, { - "x": 669.4970092773438, + "x": 422.6820068359375, "y": 274.3320007324219 }, { - "x": 667.5689697265625, + "x": 420.7539978027344, "y": 282.5899963378906 }, { - "x": 665.2930297851562, + "x": 418.47698974609375, "y": 290.7590026855469 }, { - "x": 662.6719970703125, + "x": 415.85699462890625, "y": 298.82501220703125 }, { - "x": 659.7119750976562, + "x": 412.89599609375, "y": 306.7720031738281 }, { - "x": 656.4169921875, + "x": 409.60198974609375, "y": 314.58599853515625 }, { - "x": 652.7949829101562, + "x": 405.97900390625, "y": 322.25299072265625 }, { - "x": 648.8499755859375, + "x": 402.0350036621094, "y": 329.760986328125 }, { - "x": 644.5910034179688, + "x": 397.7749938964844, "y": 337.093994140625 }, { - "x": 640.0239868164062, + "x": 393.2090148925781, "y": 344.239990234375 }, { - "x": 635.1589965820312, + "x": 388.343994140625, "y": 351.1860046386719 }, { - "x": 630.0040283203125, + "x": 383.1889953613281, "y": 357.91900634765625 }, { - "x": 624.5679931640625, + "x": 377.75299072265625, "y": 364.4280090332031 }, { - "x": 618.8610229492188, + "x": 372.0459899902344, "y": 370.70098876953125 }, { - "x": 612.8930053710938, + "x": 366.0780029296875, "y": 376.72601318359375 }, { - "x": 606.6749877929688, + "x": 359.8599853515625, "y": 382.49200439453125 }, { - "x": 600.218017578125, + "x": 353.40301513671875, "y": 387.989990234375 }, { - "x": 593.5339965820312, + "x": 346.718994140625, "y": 393.2090148925781 }, { - "x": 586.635009765625, + "x": 339.82000732421875, "y": 398.1409912109375 }, { - "x": 579.5330200195312, + "x": 332.7179870605469, "y": 402.7749938964844 }, { - "x": 572.2410278320312, + "x": 325.4259948730469, "y": 407.10400390625 }, { - "x": 564.77197265625, + "x": 317.95599365234375, "y": 411.1199951171875 }, { - "x": 557.1389770507812, + "x": 310.3240051269531, "y": 414.8160095214844 }, { - "x": 549.3569946289062, + "x": 302.5409851074219, "y": 418.1839904785156 }, { - "x": 541.43798828125, + "x": 294.62298583984375, "y": 421.2200012207031 }, { - "x": 533.3980102539062, + "x": 286.5830078125, "y": 423.9179992675781 }, { - "x": 525.2509765625, + "x": 278.4360046386719, "y": 426.2720031738281 }, { - "x": 517.0120239257812, + "x": 270.1969909667969, "y": 428.27899169921875 }, { - "x": 508.69500732421875, + "x": 261.8789978027344, "y": 429.93499755859375 }, { - "x": 500.31500244140625, + "x": 253.5, "y": 431.2359924316406 } ], @@ -853,127 +853,127 @@ "link": "", "route": [ { - "x": 447.31500244140625, + "x": 200.5, "y": 431.2359924316406 }, { - "x": 438.93499755859375, + "x": 192.1199951171875, "y": 429.93499755859375 }, { - "x": 430.6180114746094, + "x": 183.802001953125, "y": 428.27899169921875 }, { - "x": 422.37799072265625, + "x": 175.56300354003906, "y": 426.2720031738281 }, { - "x": 414.2309875488281, + "x": 167.41600036621094, "y": 423.9179992675781 }, { - "x": 406.1910095214844, + "x": 159.37600708007812, "y": 421.2200012207031 }, { - "x": 398.27301025390625, + "x": 151.45799255371094, "y": 418.1839904785156 }, { - "x": 390.4909973144531, + "x": 143.6750030517578, "y": 414.8160095214844 }, { - "x": 382.8580017089844, + "x": 136.04299926757812, "y": 411.1199951171875 }, { - "x": 375.3890075683594, + "x": 128.572998046875, "y": 407.10400390625 }, { - "x": 368.09698486328125, + "x": 121.28099822998047, "y": 402.7749938964844 }, { - "x": 360.9949951171875, + "x": 114.17900085449219, "y": 398.1409912109375 }, { - "x": 354.0950012207031, + "x": 107.27999877929688, "y": 393.2090148925781 }, { - "x": 347.4110107421875, + "x": 100.59600067138672, "y": 387.989990234375 }, { - "x": 340.9540100097656, + "x": 94.13899993896484, "y": 382.49200439453125 }, { - "x": 334.73699951171875, + "x": 87.9209976196289, "y": 376.72601318359375 }, { - "x": 328.7690124511719, + "x": 81.9530029296875, "y": 370.70098876953125 }, { - "x": 323.06201171875, + "x": 76.24600219726562, "y": 364.4280090332031 }, { - "x": 317.6260070800781, + "x": 70.80999755859375, "y": 357.91900634765625 }, { - "x": 312.4700012207031, + "x": 65.65499877929688, "y": 351.1860046386719 }, { - "x": 307.6050109863281, + "x": 60.790000915527344, "y": 344.239990234375 }, { - "x": 303.03900146484375, + "x": 56.2239990234375, "y": 337.093994140625 }, { - "x": 298.7799987792969, + "x": 51.9640007019043, "y": 329.760986328125 }, { - "x": 294.8349914550781, + "x": 48.02000045776367, "y": 322.25299072265625 }, { - "x": 291.2120056152344, + "x": 44.39699935913086, "y": 314.58599853515625 }, { - "x": 287.9179992675781, + "x": 41.10300064086914, "y": 306.7720031738281 }, { - "x": 284.9580078125, + "x": 38.141998291015625, "y": 298.82501220703125 }, { - "x": 282.3370056152344, + "x": 35.52199935913086, "y": 290.7590026855469 }, { - "x": 280.0610046386719, + "x": 33.244998931884766, "y": 282.5899963378906 }, { - "x": 278.1319885253906, + "x": 31.316999435424805, "y": 274.3320007324219 }, { - "x": 276.5559997558594, + "x": 29.740999221801758, "y": 266 } ], @@ -1009,127 +1009,127 @@ "link": "", "route": [ { - "x": 946.52001953125, + "x": 700.2050170898438, "y": 34.76300048828125 }, { - "x": 958.2249755859375, + "x": 711.9099731445312, "y": 36.68299865722656 }, { - "x": 969.7960205078125, + "x": 723.4810180664062, "y": 39.292999267578125 }, { - "x": 981.1920166015625, + "x": 734.8770141601562, "y": 42.58399963378906 }, { - "x": 992.3729858398438, + "x": 746.0570068359375, "y": 46.54600143432617 }, { - "x": 1003.2990112304688, + "x": 756.9829711914062, "y": 51.16299819946289 }, { - "x": 1013.9320068359375, + "x": 767.6170043945312, "y": 56.41899871826172 }, { - "x": 1024.2349853515625, + "x": 777.9199829101562, "y": 62.297000885009766 }, { - "x": 1034.1710205078125, + "x": 787.8560180664062, "y": 68.7760009765625 }, { - "x": 1043.7060546875, + "x": 797.3909912109375, "y": 75.83200073242188 }, { - "x": 1052.8060302734375, + "x": 806.489990234375, "y": 83.44000244140625 }, { - "x": 1061.43798828125, + "x": 815.1229858398438, "y": 91.57499694824219 }, { - "x": 1069.573974609375, + "x": 823.2579956054688, "y": 100.20800018310547 }, { - "x": 1077.1829833984375, + "x": 830.8670043945312, "y": 109.30699920654297 }, { - "x": 1084.239013671875, + "x": 837.9240112304688, "y": 118.84200286865234 }, { - "x": 1090.718017578125, + "x": 844.4030151367188, "y": 128.7779998779297 }, { - "x": 1096.595947265625, + "x": 850.281005859375, "y": 139.0800018310547 }, { - "x": 1101.85302734375, + "x": 855.5380249023438, "y": 149.71299743652344 }, { - "x": 1106.470947265625, + "x": 860.156005859375, "y": 160.63900756835938 }, { - "x": 1110.4329833984375, + "x": 864.1170043945312, "y": 171.82000732421875 }, { - "x": 1113.7249755859375, + "x": 867.4089965820312, "y": 183.21499633789062 }, { - "x": 1116.3349609375, + "x": 870.02001953125, "y": 194.78599548339844 }, { - "x": 1118.2550048828125, + "x": 871.9400024414062, "y": 206.49200439453125 }, { - "x": 1119.47802734375, + "x": 873.1630249023438, "y": 218.2899932861328 }, { - "x": 1119.9990234375, + "x": 873.6840209960938, "y": 230.13999938964844 }, { - "x": 1119.8170166015625, + "x": 873.5020141601562, "y": 242 }, { - "x": 1118.9320068359375, + "x": 872.6170043945312, "y": 253.82899475097656 }, { - "x": 1117.3480224609375, + "x": 871.031982421875, "y": 265.5840148925781 }, { - "x": 1115.0689697265625, + "x": 868.7540283203125, "y": 277.2250061035156 }, { - "x": 1112.10400390625, + "x": 865.7890014648438, "y": 288.7099914550781 }, { - "x": 1108.4639892578125, + "x": 862.1480102539062, "y": 300 } ], @@ -1165,127 +1165,127 @@ "link": "", "route": [ { - "x": 1069.3890380859375, + "x": 823.072998046875, "y": 366 }, { - "x": 1061.678955078125, + "x": 815.364013671875, "y": 374.1820068359375 }, { - "x": 1053.52197265625, + "x": 807.2069702148438, "y": 381.91900634765625 }, { - "x": 1044.9429931640625, + "x": 798.6279907226562, "y": 389.1860046386719 }, { - "x": 1035.969970703125, + "x": 789.655029296875, "y": 395.9580078125 }, { - "x": 1026.6300048828125, + "x": 780.3150024414062, "y": 402.21600341796875 }, { - "x": 1016.9530029296875, + "x": 770.6380004882812, "y": 407.9389953613281 }, { - "x": 1006.969970703125, + "x": 760.655029296875, "y": 413.1099853515625 }, { - "x": 996.7119750976562, + "x": 750.39697265625, "y": 417.71099853515625 }, { - "x": 986.2119750976562, + "x": 739.89697265625, "y": 421.7279968261719 }, { - "x": 975.5029907226562, + "x": 729.18701171875, "y": 425.1499938964844 }, { - "x": 964.6179809570312, + "x": 718.302978515625, "y": 427.9639892578125 }, { - "x": 953.5919799804688, + "x": 707.2769775390625, "y": 430.1619873046875 }, { - "x": 942.4609985351562, + "x": 696.14501953125, "y": 431.73699951171875 }, { - "x": 931.2579956054688, + "x": 684.9429931640625, "y": 432.6839904785156 }, { - "x": 920.02001953125, + "x": 673.7050170898438, "y": 433 }, { - "x": 908.781982421875, + "x": 662.4660034179688, "y": 432.6839904785156 }, { - "x": 897.5789794921875, + "x": 651.2639770507812, "y": 431.73699951171875 }, { - "x": 886.447998046875, + "x": 640.1320190429688, "y": 430.1619873046875 }, { - "x": 875.4219970703125, + "x": 629.1069946289062, "y": 427.9639892578125 }, { - "x": 864.5369873046875, + "x": 618.2219848632812, "y": 425.1499938964844 }, { - "x": 853.8280029296875, + "x": 607.5130004882812, "y": 421.7279968261719 }, { - "x": 843.3280029296875, + "x": 597.0120239257812, "y": 417.71099853515625 }, { - "x": 833.0700073242188, + "x": 586.7540283203125, "y": 413.1099853515625 }, { - "x": 823.0869750976562, + "x": 576.77099609375, "y": 407.9389953613281 }, { - "x": 813.4099731445312, + "x": 567.094970703125, "y": 402.21600341796875 }, { - "x": 804.0700073242188, + "x": 557.7550048828125, "y": 395.9580078125 }, { - "x": 795.0960083007812, + "x": 548.781005859375, "y": 389.1860046386719 }, { - "x": 786.5180053710938, + "x": 540.2020263671875, "y": 381.91900634765625 }, { - "x": 778.3610229492188, + "x": 532.0449829101562, "y": 374.1820068359375 }, { - "x": 770.6510009765625, + "x": 524.3359985351562, "y": 366 } ], @@ -1321,127 +1321,127 @@ "link": "", "route": [ { - "x": 53, + "x": 947, "y": 34.76300048828125 }, { - "x": 71.85199737548828, + "x": 965.8519897460938, "y": 38.20899963378906 }, { - "x": 90.28800201416016, + "x": 984.2880249023438, "y": 43.44499969482422 }, { - "x": 108.13800048828125, + "x": 1002.1380004882812, "y": 50.41999816894531 }, { - "x": 125.23799896240234, + "x": 1019.2379760742188, "y": 59.071998596191406 }, { - "x": 141.4320068359375, + "x": 1035.4320068359375, "y": 69.3219985961914 }, { - "x": 156.5709991455078, + "x": 1050.571044921875, "y": 81.0739974975586 }, { - "x": 170.51499938964844, + "x": 1064.5150146484375, "y": 94.22100067138672 }, { - "x": 183.13699340820312, + "x": 1077.136962890625, "y": 108.64199829101562 }, { - "x": 194.32000732421875, + "x": 1088.3199462890625, "y": 124.20500183105469 }, { - "x": 203.96299743652344, + "x": 1097.9630126953125, "y": 140.76800537109375 }, { - "x": 211.9759979248047, + "x": 1105.9759521484375, "y": 158.177001953125 }, { - "x": 218.28599548339844, + "x": 1112.2860107421875, "y": 176.2729949951172 }, { - "x": 222.8350067138672, + "x": 1116.8349609375, "y": 194.88999938964844 }, { - "x": 225.58099365234375, + "x": 1119.5810546875, "y": 213.85699462890625 }, { - "x": 226.5, + "x": 1120.5, "y": 233 }, { - "x": 225.58099365234375, + "x": 1119.5810546875, "y": 252.14199829101562 }, { - "x": 222.8350067138672, + "x": 1116.8349609375, "y": 271.1090087890625 }, { - "x": 218.28599548339844, + "x": 1112.2860107421875, "y": 289.72601318359375 }, { - "x": 211.9759979248047, + "x": 1105.9759521484375, "y": 307.8219909667969 }, { - "x": 203.96299743652344, + "x": 1097.9630126953125, "y": 325.2309875488281 }, { - "x": 194.32000732421875, + "x": 1088.3199462890625, "y": 341.79400634765625 }, { - "x": 183.13699340820312, + "x": 1077.136962890625, "y": 357.35699462890625 }, { - "x": 170.51499938964844, + "x": 1064.5150146484375, "y": 371.77801513671875 }, { - "x": 156.5709991455078, + "x": 1050.571044921875, "y": 384.92498779296875 }, { - "x": 141.4320068359375, + "x": 1035.4320068359375, "y": 396.677001953125 }, { - "x": 125.23799896240234, + "x": 1019.2379760742188, "y": 406.927001953125 }, { - "x": 108.13800048828125, + "x": 1002.1380004882812, "y": 415.5790100097656 }, { - "x": 90.28800201416016, + "x": 984.2880249023438, "y": 422.5539855957031 }, { - "x": 71.85199737548828, + "x": 965.8519897460938, "y": 427.7900085449219 }, { - "x": 53, + "x": 947, "y": 431.2359924316406 } ], diff --git a/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg b/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg index 5f5ec34863..acf45b9da1 100644 --- a/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-diagram/tala/sketch.exp.svg @@ -1,9 +1,9 @@ -abcdabcab - + .d2-916405248 .fill-N1{fill:#0A0F25;} + .d2-916405248 .fill-N7{fill:#FFFFFF;} + .d2-916405248 .fill-B1{fill:#0D32B2;} + .d2-916405248 .fill-B5{fill:#EDF0FD;} + .d2-916405248 .stroke-B1{stroke:#0D32B2;} + .d2-916405248 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>abcdabcab + \ No newline at end of file From 3b89459c33b8f3f2d4e9e06d8aca3da03de78f96 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Mon, 14 Sep 2026 09:49:44 +0900 Subject: [PATCH 15/17] Route self loops in cycle diagrams A self edge shares one shape between its source and its destination, so the arc geometry degenerates: the two centres coincide, the sweep is zero, and the fallback clips both endpoints to the same point. The route was left as two identical points, which the exporter wrote out as d="M NaN NaN L NaN NaN" and which, now that the render target is validated, fails the whole render with "route[0:1] must have a finite, non-zero segment length". Give the self edge a loop of its own instead: a circle tangent to the shape border on the far side from the centre of the ring, sampled the same way the arcs are. Shapes that sit on the centre have no outward direction, so their loop goes straight up. The txtar case is a new entry rather than a fourth board on cycle-diagram, because adding a board there moves the other three and rewrites goldens that have nothing to do with this. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0174tmEsVYhznCbNSYEZWett --- d2layouts/d2cycle/layout.go | 50 ++ d2layouts/d2cycle/layout_test.go | 87 +++ .../cycle-self-loop/dagre/board.exp.json | 496 ++++++++++++++++++ .../cycle-self-loop/dagre/sketch.exp.svg | 29 + .../txtar/cycle-self-loop/elk/board.exp.json | 496 ++++++++++++++++++ .../txtar/cycle-self-loop/elk/sketch.exp.svg | 29 + .../txtar/cycle-self-loop/tala/board.exp.json | 496 ++++++++++++++++++ .../txtar/cycle-self-loop/tala/sketch.exp.svg | 29 + e2etests/txtar.txt | 7 + 9 files changed, 1719 insertions(+) create mode 100644 d2layouts/d2cycle/layout_test.go create mode 100644 e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json create mode 100644 e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index d04075a704..ea4b323c28 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -15,6 +15,9 @@ const ( MIN_RADIUS = 200 PADDING = 20 ARC_STEPS = 30 + // SELF_LOOP_MIN_RADIUS keeps the loop of a self edge visible even when the + // shape it hangs off is tiny. + SELF_LOOP_MIN_RADIUS = 20 ) // Layout arranges nodes in a circle and routes each edge as a circular arc @@ -126,6 +129,11 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { return } + if edge.Src == edge.Dst { + selfLoopRoute(edge) + return + } + srcCenter := edge.Src.Center() dstCenter := edge.Dst.Center() origin := geo.NewPoint(0, 0) @@ -169,6 +177,48 @@ func createCircularArc(edge *d2graph.Edge, radius float64) { edge.IsCurve = true } +// selfLoopRoute routes an edge whose source and destination are the same shape +// as a small circle sitting just outside that shape, on the far side from the +// centre of the layout ring. The circular arc code cannot express a self loop: +// the two centres coincide, so the sweep is zero and every clipped endpoint +// collapses onto the same point. That leaves a zero length route, which the +// renderer turns into NaN coordinates and the render target validator rejects. +func selfLoopRoute(edge *d2graph.Edge) { + box := edge.Src.Box + center := edge.Src.Center() + + // The ring is centred on the origin, so the shape centre already points + // away from it. A shape sitting exactly on the origin has no outward + // direction of its own; send its loop straight up. + outX, outY := center.X, center.Y + if norm := math.Hypot(outX, outY); norm > 0 { + outX, outY = outX/norm, outY/norm + } else { + outX, outY = 0, -1 + } + + reach := box.Width + box.Height + far := geo.NewPoint(center.X+outX*reach, center.Y+outY*reach) + anchor := clipToShapeBorder(edge.Src.ToShape(), box, center, far) + + loopRadius := math.Max(SELF_LOOP_MIN_RADIUS, math.Min(box.Width, box.Height)/3) + loopCenter := geo.NewPoint(anchor.X+outX*loopRadius, anchor.Y+outY*loopRadius) + // anchor lies on the loop, so start and finish the sweep there. + startAngle := math.Atan2(anchor.Y-loopCenter.Y, anchor.X-loopCenter.X) + + path := make([]*geo.Point, 0, ARC_STEPS+1) + for i := 0; i <= ARC_STEPS; i++ { + angle := startAngle + 2*math.Pi*float64(i)/float64(ARC_STEPS) + path = append(path, geo.NewPoint( + loopCenter.X+loopRadius*math.Cos(angle), + loopCenter.Y+loopRadius*math.Sin(angle), + )) + } + + edge.Route = path + edge.IsCurve = true +} + // 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 diff --git a/d2layouts/d2cycle/layout_test.go b/d2layouts/d2cycle/layout_test.go new file mode 100644 index 0000000000..78ee086bfc --- /dev/null +++ b/d2layouts/d2cycle/layout_test.go @@ -0,0 +1,87 @@ +package d2cycle + +import ( + "math" + "testing" + + "github.com/d2lang/d2/d2graph" + "github.com/d2lang/d2/lib/geo" +) + +// A curved connection is rendered as a chain of cubic segments, so the render +// target validator requires 1+3n points on its route. Every route this package +// emits samples ARC_STEPS+1 points, which ties the constant to that rule: the +// current value of 30 satisfies it, but nothing in the sampling loops would +// stop a later edit from picking a value that rejects every arc. +func TestARCStepsSamplesACubicPointCount(t *testing.T) { + points := ARC_STEPS + 1 + if (points-1)%3 != 0 { + t.Fatalf("ARC_STEPS = %d samples %d points, but a curved route needs 1+3n; pick a multiple of 3", ARC_STEPS, points) + } +} + +// A self loop shares one centre between source and destination, so the arc +// geometry degenerates: the sweep is zero and both clipped endpoints land on +// the same point. Routing it as a straight line therefore yields a zero length +// route, which renders as NaN and is rejected by the render target validator. +func TestSelfLoopRouteIsRenderable(t *testing.T) { + for _, tc := range []struct { + name string + cx, cy, w, h float64 + }{ + {"on the ring", MIN_RADIUS, 0, 100, 60}, + {"above the ring centre", 0, -MIN_RADIUS, 100, 60}, + {"off axis", 141, -141, 100, 60}, + {"tiny shape", MIN_RADIUS, 0, 6, 6}, + {"at the ring centre", 0, 0, 100, 60}, + } { + t.Run(tc.name, func(t *testing.T) { + obj := boxedObject(tc.cx, tc.cy, tc.w, tc.h) + edge := &d2graph.Edge{Src: obj, Dst: obj} + + createCircularArc(edge, MIN_RADIUS) + + if !edge.IsCurve { + t.Error("a self loop must be marked as a curve") + } + if got, want := len(edge.Route), ARC_STEPS+1; got != want { + t.Fatalf("route has %d points, want %d", got, want) + } + for i, point := range edge.Route { + if math.IsNaN(point.X) || math.IsNaN(point.Y) || math.IsInf(point.X, 0) || math.IsInf(point.Y, 0) { + t.Fatalf("route[%d] = (%v, %v), want a finite point", i, point.X, point.Y) + } + if i == 0 { + continue + } + if previous := edge.Route[i-1]; previous.X == point.X && previous.Y == point.Y { + t.Fatalf("route[%d:%d] is a zero length segment at (%v, %v)", i-1, i, point.X, point.Y) + } + } + + // A loop squashed onto a line is not a loop. Both sides of its + // bounding box have to span the loop, which is at least + // SELF_LOOP_MIN_RADIUS across. + width, height := routeBounds(edge.Route) + if width < SELF_LOOP_MIN_RADIUS || height < SELF_LOOP_MIN_RADIUS { + t.Errorf("loop spans %v x %v, want at least %v on both sides", width, height, SELF_LOOP_MIN_RADIUS) + } + }) + } +} + +func boxedObject(centerX, centerY, width, height float64) *d2graph.Object { + return &d2graph.Object{ + Box: geo.NewBox(geo.NewPoint(centerX-width/2, centerY-height/2), width, height), + } +} + +func routeBounds(route []*geo.Point) (width, height float64) { + minX, minY := math.Inf(1), math.Inf(1) + maxX, maxY := math.Inf(-1), math.Inf(-1) + for _, point := range route { + minX, maxX = math.Min(minX, point.X), math.Max(maxX, point.X) + minY, maxY = math.Min(minY, point.Y), math.Max(maxY, point.Y) + } + return maxX - minX, maxY - minY +} diff --git a/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json new file mode 100644 index 0000000000..e488d1a14f --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json @@ -0,0 +1,496 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", + "shapes": [ + { + "id": "x", + "type": "cycle", + "pos": { + "x": 0, + "y": 0 + }, + "width": 227, + "height": 506, + "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": "x.a", + "type": "rectangle", + "pos": { + "x": 0, + "y": 40 + }, + "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": "x.b", + "type": "rectangle", + "pos": { + "x": 0, + "y": 440 + }, + "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": "x.(a -> a)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.a", + "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": 26.5, + "y": 40 + }, + { + "x": 22.340999603271484, + "y": 39.5620002746582 + }, + { + "x": 18.364999771118164, + "y": 38.27000045776367 + }, + { + "x": 14.744000434875488, + "y": 36.18000030517578 + }, + { + "x": 11.63700008392334, + "y": 33.38199996948242 + }, + { + "x": 9.178999900817871, + "y": 30 + }, + { + "x": 7.478000164031982, + "y": 26.18000030517578 + }, + { + "x": 6.609000205993652, + "y": 22.09000015258789 + }, + { + "x": 6.609000205993652, + "y": 17.909000396728516 + }, + { + "x": 7.478000164031982, + "y": 13.819000244140625 + }, + { + "x": 9.178999900817871, + "y": 10 + }, + { + "x": 11.63700008392334, + "y": 6.617000102996826 + }, + { + "x": 14.744000434875488, + "y": 3.819000005722046 + }, + { + "x": 18.364999771118164, + "y": 1.7289999723434448 + }, + { + "x": 22.340999603271484, + "y": 0.43700000643730164 + }, + { + "x": 26.499000549316406, + "y": 0 + }, + { + "x": 30.658000946044922, + "y": 0.43700000643730164 + }, + { + "x": 34.63399887084961, + "y": 1.7289999723434448 + }, + { + "x": 38.255001068115234, + "y": 3.819000005722046 + }, + { + "x": 41.36199951171875, + "y": 6.617000102996826 + }, + { + "x": 43.81999969482422, + "y": 10 + }, + { + "x": 45.520999908447266, + "y": 13.819000244140625 + }, + { + "x": 46.38999938964844, + "y": 17.909000396728516 + }, + { + "x": 46.38999938964844, + "y": 22.09000015258789 + }, + { + "x": 45.520999908447266, + "y": 26.18000030517578 + }, + { + "x": 43.81999969482422, + "y": 30 + }, + { + "x": 41.36199951171875, + "y": 33.38199996948242 + }, + { + "x": 38.255001068115234, + "y": 36.18000030517578 + }, + { + "x": 34.63399887084961, + "y": 38.27000045776367 + }, + { + "x": 30.658000946044922, + "y": 39.5620002746582 + }, + { + "x": 26.5, + "y": 40 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "x.(a -> b)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.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": 53, + "y": 74.76300048828125 + }, + { + "x": 71.85199737548828, + "y": 78.20899963378906 + }, + { + "x": 90.28800201416016, + "y": 83.44499969482422 + }, + { + "x": 108.13800048828125, + "y": 90.41999816894531 + }, + { + "x": 125.23799896240234, + "y": 99.0719985961914 + }, + { + "x": 141.4320068359375, + "y": 109.3219985961914 + }, + { + "x": 156.5709991455078, + "y": 121.0739974975586 + }, + { + "x": 170.51499938964844, + "y": 134.2209930419922 + }, + { + "x": 183.13699340820312, + "y": 148.64199829101562 + }, + { + "x": 194.32000732421875, + "y": 164.2050018310547 + }, + { + "x": 203.96299743652344, + "y": 180.76800537109375 + }, + { + "x": 211.9759979248047, + "y": 198.177001953125 + }, + { + "x": 218.28599548339844, + "y": 216.2729949951172 + }, + { + "x": 222.8350067138672, + "y": 234.88999938964844 + }, + { + "x": 225.58099365234375, + "y": 253.85699462890625 + }, + { + "x": 226.5, + "y": 273 + }, + { + "x": 225.58099365234375, + "y": 292.1419982910156 + }, + { + "x": 222.8350067138672, + "y": 311.1090087890625 + }, + { + "x": 218.28599548339844, + "y": 329.72601318359375 + }, + { + "x": 211.9759979248047, + "y": 347.8219909667969 + }, + { + "x": 203.96299743652344, + "y": 365.2309875488281 + }, + { + "x": 194.32000732421875, + "y": 381.79400634765625 + }, + { + "x": 183.13699340820312, + "y": 397.35699462890625 + }, + { + "x": 170.51499938964844, + "y": 411.77801513671875 + }, + { + "x": 156.5709991455078, + "y": 424.92498779296875 + }, + { + "x": 141.4320068359375, + "y": 436.677001953125 + }, + { + "x": 125.23799896240234, + "y": 446.927001953125 + }, + { + "x": 108.13800048828125, + "y": 455.5790100097656 + }, + { + "x": 90.28800201416016, + "y": 462.5539855957031 + }, + { + "x": 71.85199737548828, + "y": 467.7900085449219 + }, + { + "x": 53, + "y": 471.2359924316406 + } + ], + "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-self-loop/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg new file mode 100644 index 0000000000..fbfe5672ae --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg @@ -0,0 +1,29 @@ +ab + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json new file mode 100644 index 0000000000..3ff2d8006a --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json @@ -0,0 +1,496 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", + "shapes": [ + { + "id": "x", + "type": "cycle", + "pos": { + "x": 12, + "y": 12 + }, + "width": 227, + "height": 506, + "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": "x.a", + "type": "rectangle", + "pos": { + "x": 12, + "y": 52 + }, + "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": "x.b", + "type": "rectangle", + "pos": { + "x": 12, + "y": 452 + }, + "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": "x.(a -> a)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.a", + "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": 38.5, + "y": 52 + }, + { + "x": 34.340999603271484, + "y": 51.5620002746582 + }, + { + "x": 30.364999771118164, + "y": 50.27000045776367 + }, + { + "x": 26.743999481201172, + "y": 48.18000030517578 + }, + { + "x": 23.636999130249023, + "y": 45.38199996948242 + }, + { + "x": 21.179000854492188, + "y": 42 + }, + { + "x": 19.47800064086914, + "y": 38.18000030517578 + }, + { + "x": 18.608999252319336, + "y": 34.09000015258789 + }, + { + "x": 18.608999252319336, + "y": 29.909000396728516 + }, + { + "x": 19.47800064086914, + "y": 25.819000244140625 + }, + { + "x": 21.179000854492188, + "y": 22 + }, + { + "x": 23.636999130249023, + "y": 18.617000579833984 + }, + { + "x": 26.743999481201172, + "y": 15.819000244140625 + }, + { + "x": 30.364999771118164, + "y": 13.729000091552734 + }, + { + "x": 34.340999603271484, + "y": 12.437000274658203 + }, + { + "x": 38.5, + "y": 12 + }, + { + "x": 42.65800094604492, + "y": 12.437000274658203 + }, + { + "x": 46.63399887084961, + "y": 13.729000091552734 + }, + { + "x": 50.255001068115234, + "y": 15.819000244140625 + }, + { + "x": 53.36199951171875, + "y": 18.617000579833984 + }, + { + "x": 55.81999969482422, + "y": 22 + }, + { + "x": 57.520999908447266, + "y": 25.819000244140625 + }, + { + "x": 58.38999938964844, + "y": 29.909000396728516 + }, + { + "x": 58.38999938964844, + "y": 34.09000015258789 + }, + { + "x": 57.520999908447266, + "y": 38.18000030517578 + }, + { + "x": 55.81999969482422, + "y": 42 + }, + { + "x": 53.36199951171875, + "y": 45.38199996948242 + }, + { + "x": 50.255001068115234, + "y": 48.18000030517578 + }, + { + "x": 46.63399887084961, + "y": 50.27000045776367 + }, + { + "x": 42.65800094604492, + "y": 51.5620002746582 + }, + { + "x": 38.5, + "y": 52 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "x.(a -> b)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.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": 65, + "y": 86.76300048828125 + }, + { + "x": 83.85199737548828, + "y": 90.20899963378906 + }, + { + "x": 102.28800201416016, + "y": 95.44499969482422 + }, + { + "x": 120.13800048828125, + "y": 102.41999816894531 + }, + { + "x": 137.23800659179688, + "y": 111.0719985961914 + }, + { + "x": 153.4320068359375, + "y": 121.3219985961914 + }, + { + "x": 168.5709991455078, + "y": 133.07400512695312 + }, + { + "x": 182.51499938964844, + "y": 146.2209930419922 + }, + { + "x": 195.13699340820312, + "y": 160.64199829101562 + }, + { + "x": 206.32000732421875, + "y": 176.2050018310547 + }, + { + "x": 215.96299743652344, + "y": 192.76800537109375 + }, + { + "x": 223.9759979248047, + "y": 210.177001953125 + }, + { + "x": 230.28599548339844, + "y": 228.2729949951172 + }, + { + "x": 234.8350067138672, + "y": 246.88999938964844 + }, + { + "x": 237.58099365234375, + "y": 265.85699462890625 + }, + { + "x": 238.5, + "y": 285 + }, + { + "x": 237.58099365234375, + "y": 304.1419982910156 + }, + { + "x": 234.8350067138672, + "y": 323.1090087890625 + }, + { + "x": 230.28599548339844, + "y": 341.72601318359375 + }, + { + "x": 223.9759979248047, + "y": 359.8219909667969 + }, + { + "x": 215.96299743652344, + "y": 377.2309875488281 + }, + { + "x": 206.32000732421875, + "y": 393.79400634765625 + }, + { + "x": 195.13699340820312, + "y": 409.35699462890625 + }, + { + "x": 182.51499938964844, + "y": 423.77801513671875 + }, + { + "x": 168.5709991455078, + "y": 436.92498779296875 + }, + { + "x": 153.4320068359375, + "y": 448.677001953125 + }, + { + "x": 137.23800659179688, + "y": 458.927001953125 + }, + { + "x": 120.13800048828125, + "y": 467.5790100097656 + }, + { + "x": 102.28800201416016, + "y": 474.5539855957031 + }, + { + "x": 83.85199737548828, + "y": 479.7900085449219 + }, + { + "x": 65, + "y": 483.2359924316406 + } + ], + "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-self-loop/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg new file mode 100644 index 0000000000..3d62da287c --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg @@ -0,0 +1,29 @@ +ab + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json new file mode 100644 index 0000000000..e488d1a14f --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json @@ -0,0 +1,496 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "monoFontFamily": "SourceCodePro", + "shapes": [ + { + "id": "x", + "type": "cycle", + "pos": { + "x": 0, + "y": 0 + }, + "width": 227, + "height": 506, + "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": "x.a", + "type": "rectangle", + "pos": { + "x": 0, + "y": 40 + }, + "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": "x.b", + "type": "rectangle", + "pos": { + "x": 0, + "y": 440 + }, + "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": "x.(a -> a)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.a", + "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": 26.5, + "y": 40 + }, + { + "x": 22.340999603271484, + "y": 39.5620002746582 + }, + { + "x": 18.364999771118164, + "y": 38.27000045776367 + }, + { + "x": 14.744000434875488, + "y": 36.18000030517578 + }, + { + "x": 11.63700008392334, + "y": 33.38199996948242 + }, + { + "x": 9.178999900817871, + "y": 30 + }, + { + "x": 7.478000164031982, + "y": 26.18000030517578 + }, + { + "x": 6.609000205993652, + "y": 22.09000015258789 + }, + { + "x": 6.609000205993652, + "y": 17.909000396728516 + }, + { + "x": 7.478000164031982, + "y": 13.819000244140625 + }, + { + "x": 9.178999900817871, + "y": 10 + }, + { + "x": 11.63700008392334, + "y": 6.617000102996826 + }, + { + "x": 14.744000434875488, + "y": 3.819000005722046 + }, + { + "x": 18.364999771118164, + "y": 1.7289999723434448 + }, + { + "x": 22.340999603271484, + "y": 0.43700000643730164 + }, + { + "x": 26.499000549316406, + "y": 0 + }, + { + "x": 30.658000946044922, + "y": 0.43700000643730164 + }, + { + "x": 34.63399887084961, + "y": 1.7289999723434448 + }, + { + "x": 38.255001068115234, + "y": 3.819000005722046 + }, + { + "x": 41.36199951171875, + "y": 6.617000102996826 + }, + { + "x": 43.81999969482422, + "y": 10 + }, + { + "x": 45.520999908447266, + "y": 13.819000244140625 + }, + { + "x": 46.38999938964844, + "y": 17.909000396728516 + }, + { + "x": 46.38999938964844, + "y": 22.09000015258789 + }, + { + "x": 45.520999908447266, + "y": 26.18000030517578 + }, + { + "x": 43.81999969482422, + "y": 30 + }, + { + "x": 41.36199951171875, + "y": 33.38199996948242 + }, + { + "x": 38.255001068115234, + "y": 36.18000030517578 + }, + { + "x": 34.63399887084961, + "y": 38.27000045776367 + }, + { + "x": 30.658000946044922, + "y": 39.5620002746582 + }, + { + "x": 26.5, + "y": 40 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "x.(a -> b)[0]", + "src": "x.a", + "srcArrow": "none", + "dst": "x.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": 53, + "y": 74.76300048828125 + }, + { + "x": 71.85199737548828, + "y": 78.20899963378906 + }, + { + "x": 90.28800201416016, + "y": 83.44499969482422 + }, + { + "x": 108.13800048828125, + "y": 90.41999816894531 + }, + { + "x": 125.23799896240234, + "y": 99.0719985961914 + }, + { + "x": 141.4320068359375, + "y": 109.3219985961914 + }, + { + "x": 156.5709991455078, + "y": 121.0739974975586 + }, + { + "x": 170.51499938964844, + "y": 134.2209930419922 + }, + { + "x": 183.13699340820312, + "y": 148.64199829101562 + }, + { + "x": 194.32000732421875, + "y": 164.2050018310547 + }, + { + "x": 203.96299743652344, + "y": 180.76800537109375 + }, + { + "x": 211.9759979248047, + "y": 198.177001953125 + }, + { + "x": 218.28599548339844, + "y": 216.2729949951172 + }, + { + "x": 222.8350067138672, + "y": 234.88999938964844 + }, + { + "x": 225.58099365234375, + "y": 253.85699462890625 + }, + { + "x": 226.5, + "y": 273 + }, + { + "x": 225.58099365234375, + "y": 292.1419982910156 + }, + { + "x": 222.8350067138672, + "y": 311.1090087890625 + }, + { + "x": 218.28599548339844, + "y": 329.72601318359375 + }, + { + "x": 211.9759979248047, + "y": 347.8219909667969 + }, + { + "x": 203.96299743652344, + "y": 365.2309875488281 + }, + { + "x": 194.32000732421875, + "y": 381.79400634765625 + }, + { + "x": 183.13699340820312, + "y": 397.35699462890625 + }, + { + "x": 170.51499938964844, + "y": 411.77801513671875 + }, + { + "x": 156.5709991455078, + "y": 424.92498779296875 + }, + { + "x": 141.4320068359375, + "y": 436.677001953125 + }, + { + "x": 125.23799896240234, + "y": 446.927001953125 + }, + { + "x": 108.13800048828125, + "y": 455.5790100097656 + }, + { + "x": 90.28800201416016, + "y": 462.5539855957031 + }, + { + "x": 71.85199737548828, + "y": 467.7900085449219 + }, + { + "x": 53, + "y": 471.2359924316406 + } + ], + "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-self-loop/tala/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg new file mode 100644 index 0000000000..fbfe5672ae --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg @@ -0,0 +1,29 @@ +ab + + + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index 70a3985e5b..a726fb704b 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -1773,6 +1773,13 @@ style: {fill-pattern: dots; fill:"radial-gradient(#fbfbf8, #e3e3f0)"; stroke: "# a->b +-- cycle-self-loop -- +x: "" { + shape: cycle + a -> a + a -> b +} + -- cycle-diagram -- 1: "" { shape: cycle From 0be80174180f43880ff090a8c26a6ae89a88b83a Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Mon, 14 Sep 2026 09:52:39 +0900 Subject: [PATCH 16/17] Keep the cycle radius finite when the ring holds one shape calculateRadius spaces the shapes around the ring by sin(pi/n), and sin(pi/1) is 1.2e-16 rather than 0, so a cycle holding a single shape came out with a radius of about 5.7e17. At that magnitude the gap between representable doubles is wider than the shapes themselves, so anything computed on the ring collapses. It was visible before this branch as a board written 4.3e17 pixels tall; fitting the container to its contents hid that, but the arithmetic was still wrong underneath, and the self loop of a lone shape came out flattened onto y = 0 while still passing validation. One shape needs no room for neighbours, so return the minimum radius. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0174tmEsVYhznCbNSYEZWett --- d2layouts/d2cycle/layout.go | 7 + d2layouts/d2cycle/layout_test.go | 14 + .../cycle-self-loop/dagre/board.exp.json | 239 ++++++++++++++++++ .../cycle-self-loop/dagre/sketch.exp.svg | 22 +- .../txtar/cycle-self-loop/elk/board.exp.json | 239 ++++++++++++++++++ .../txtar/cycle-self-loop/elk/sketch.exp.svg | 22 +- .../txtar/cycle-self-loop/tala/board.exp.json | 239 ++++++++++++++++++ .../txtar/cycle-self-loop/tala/sketch.exp.svg | 22 +- e2etests/txtar.txt | 4 + 9 files changed, 775 insertions(+), 33 deletions(-) diff --git a/d2layouts/d2cycle/layout.go b/d2layouts/d2cycle/layout.go index ea4b323c28..5b687d96c0 100644 --- a/d2layouts/d2cycle/layout.go +++ b/d2layouts/d2cycle/layout.go @@ -100,6 +100,13 @@ func calculateRadius(objects []*d2graph.Object) float64 { size := math.Max(obj.Box.Width, obj.Box.Height) maxSize = math.Max(maxSize, size) } + if numObjects < 2 { + // sin(pi/1) is zero to within rounding, so the expression below + // returns about 1e17. Every coordinate on the ring is then spaced + // further apart than the shapes themselves, and the arcs computed + // from them collapse. One shape needs no room for neighbours. + return MIN_RADIUS + } minRadius := (maxSize/2.0 + PADDING) / math.Sin(math.Pi/numObjects) return math.Max(minRadius, MIN_RADIUS) } diff --git a/d2layouts/d2cycle/layout_test.go b/d2layouts/d2cycle/layout_test.go index 78ee086bfc..6282775bd4 100644 --- a/d2layouts/d2cycle/layout_test.go +++ b/d2layouts/d2cycle/layout_test.go @@ -20,6 +20,20 @@ func TestARCStepsSamplesACubicPointCount(t *testing.T) { } } +// A cycle holding a single shape has no neighbours to make room for, and the +// spacing formula is undefined there: sin(pi/1) rounds to 1.2e-16, which sends +// the radius to about 1e17. Coordinates that large are spaced further apart +// than the shapes are wide, so anything computed on the ring degenerates. +func TestCalculateRadiusStaysFiniteForOneObject(t *testing.T) { + obj := boxedObject(0, 0, 100, 60) + + radius := calculateRadius([]*d2graph.Object{obj}) + + if radius != MIN_RADIUS { + t.Fatalf("radius for a single object = %v, want %v", radius, MIN_RADIUS) + } +} + // A self loop shares one centre between source and destination, so the arc // geometry degenerates: the sweep is zero and both clipped endpoints land on // the same point. Routing it as a straight line therefore yields a zero length diff --git a/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json index e488d1a14f..dceeba481b 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json +++ b/e2etests/testdata/txtar/cycle-self-loop/dagre/board.exp.json @@ -136,6 +136,89 @@ "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, "level": 2 + }, + { + "id": "y", + "type": "cycle", + "pos": { + "x": 287, + "y": 200 + }, + "width": 53, + "height": 106, + "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": "y.a", + "type": "rectangle", + "pos": { + "x": 287, + "y": 240 + }, + "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 } ], "connections": [ @@ -450,6 +533,162 @@ "tooltip": "", "icon": null, "zIndex": 0 + }, + { + "id": "y.(a -> a)[0]", + "src": "y.a", + "srcArrow": "none", + "dst": "y.a", + "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": 313.5, + "y": 240 + }, + { + "x": 309.34100341796875, + "y": 239.56199645996094 + }, + { + "x": 305.364990234375, + "y": 238.27000427246094 + }, + { + "x": 301.7439880371094, + "y": 236.17999267578125 + }, + { + "x": 298.6369934082031, + "y": 233.3820037841797 + }, + { + "x": 296.1789855957031, + "y": 230 + }, + { + "x": 294.4779968261719, + "y": 226.17999267578125 + }, + { + "x": 293.6090087890625, + "y": 222.08999633789062 + }, + { + "x": 293.6090087890625, + "y": 217.90899658203125 + }, + { + "x": 294.4779968261719, + "y": 213.81900024414062 + }, + { + "x": 296.1789855957031, + "y": 210 + }, + { + "x": 298.6369934082031, + "y": 206.61700439453125 + }, + { + "x": 301.7439880371094, + "y": 203.81900024414062 + }, + { + "x": 305.364990234375, + "y": 201.72900390625 + }, + { + "x": 309.34100341796875, + "y": 200.43699645996094 + }, + { + "x": 313.5, + "y": 200 + }, + { + "x": 317.6579895019531, + "y": 200.43699645996094 + }, + { + "x": 321.6340026855469, + "y": 201.72900390625 + }, + { + "x": 325.2550048828125, + "y": 203.81900024414062 + }, + { + "x": 328.36199951171875, + "y": 206.61700439453125 + }, + { + "x": 330.82000732421875, + "y": 210 + }, + { + "x": 332.52099609375, + "y": 213.81900024414062 + }, + { + "x": 333.3900146484375, + "y": 217.90899658203125 + }, + { + "x": 333.3900146484375, + "y": 222.08999633789062 + }, + { + "x": 332.52099609375, + "y": 226.17999267578125 + }, + { + "x": 330.82000732421875, + "y": 230 + }, + { + "x": 328.36199951171875, + "y": 233.3820037841797 + }, + { + "x": 325.2550048828125, + "y": 236.17999267578125 + }, + { + "x": 321.6340026855469, + "y": 238.27000427246094 + }, + { + "x": 317.6579895019531, + "y": 239.56199645996094 + }, + { + "x": 313.5, + "y": 240 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 } ], "root": { diff --git a/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg index fbfe5672ae..363bcaadc7 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-self-loop/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ -ab - + .d2-3744719484 .fill-N1{fill:#0A0F25;} + .d2-3744719484 .fill-N7{fill:#FFFFFF;} + .d2-3744719484 .fill-B1{fill:#0D32B2;} + .d2-3744719484 .fill-B5{fill:#EDF0FD;} + .d2-3744719484 .stroke-B1{stroke:#0D32B2;} + .d2-3744719484 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>aba + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json index 3ff2d8006a..4c52a7c163 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json +++ b/e2etests/testdata/txtar/cycle-self-loop/elk/board.exp.json @@ -136,6 +136,89 @@ "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, "level": 2 + }, + { + "id": "y", + "type": "cycle", + "pos": { + "x": 259, + "y": 212 + }, + "width": 53, + "height": 106, + "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": "y.a", + "type": "rectangle", + "pos": { + "x": 259, + "y": 252 + }, + "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 } ], "connections": [ @@ -450,6 +533,162 @@ "tooltip": "", "icon": null, "zIndex": 0 + }, + { + "id": "y.(a -> a)[0]", + "src": "y.a", + "srcArrow": "none", + "dst": "y.a", + "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": 285.5, + "y": 252 + }, + { + "x": 281.34100341796875, + "y": 251.56199645996094 + }, + { + "x": 277.364990234375, + "y": 250.27000427246094 + }, + { + "x": 273.7439880371094, + "y": 248.17999267578125 + }, + { + "x": 270.6369934082031, + "y": 245.3820037841797 + }, + { + "x": 268.1789855957031, + "y": 242 + }, + { + "x": 266.4779968261719, + "y": 238.17999267578125 + }, + { + "x": 265.6090087890625, + "y": 234.08999633789062 + }, + { + "x": 265.6090087890625, + "y": 229.90899658203125 + }, + { + "x": 266.4779968261719, + "y": 225.81900024414062 + }, + { + "x": 268.1789855957031, + "y": 222 + }, + { + "x": 270.6369934082031, + "y": 218.61700439453125 + }, + { + "x": 273.7439880371094, + "y": 215.81900024414062 + }, + { + "x": 277.364990234375, + "y": 213.72900390625 + }, + { + "x": 281.34100341796875, + "y": 212.43699645996094 + }, + { + "x": 285.5, + "y": 212 + }, + { + "x": 289.6579895019531, + "y": 212.43699645996094 + }, + { + "x": 293.6340026855469, + "y": 213.72900390625 + }, + { + "x": 297.2550048828125, + "y": 215.81900024414062 + }, + { + "x": 300.36199951171875, + "y": 218.61700439453125 + }, + { + "x": 302.82000732421875, + "y": 222 + }, + { + "x": 304.52099609375, + "y": 225.81900024414062 + }, + { + "x": 305.3900146484375, + "y": 229.90899658203125 + }, + { + "x": 305.3900146484375, + "y": 234.08999633789062 + }, + { + "x": 304.52099609375, + "y": 238.17999267578125 + }, + { + "x": 302.82000732421875, + "y": 242 + }, + { + "x": 300.36199951171875, + "y": 245.3820037841797 + }, + { + "x": 297.2550048828125, + "y": 248.17999267578125 + }, + { + "x": 293.6340026855469, + "y": 250.27000427246094 + }, + { + "x": 289.6579895019531, + "y": 251.56199645996094 + }, + { + "x": 285.5, + "y": 252 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 } ], "root": { diff --git a/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg index 3d62da287c..86dc32353c 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-self-loop/elk/sketch.exp.svg @@ -1,9 +1,9 @@ -ab - + .d2-3354009165 .fill-N1{fill:#0A0F25;} + .d2-3354009165 .fill-N7{fill:#FFFFFF;} + .d2-3354009165 .fill-B1{fill:#0D32B2;} + .d2-3354009165 .fill-B5{fill:#EDF0FD;} + .d2-3354009165 .stroke-B1{stroke:#0D32B2;} + .d2-3354009165 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>aba + \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json b/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json index e488d1a14f..7455468e4d 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json +++ b/e2etests/testdata/txtar/cycle-self-loop/tala/board.exp.json @@ -136,6 +136,89 @@ "labelPosition": "INSIDE_MIDDLE_CENTER", "zIndex": 0, "level": 2 + }, + { + "id": "y", + "type": "cycle", + "pos": { + "x": 247, + "y": 0 + }, + "width": 53, + "height": 106, + "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": "y.a", + "type": "rectangle", + "pos": { + "x": 247, + "y": 40 + }, + "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 } ], "connections": [ @@ -450,6 +533,162 @@ "tooltip": "", "icon": null, "zIndex": 0 + }, + { + "id": "y.(a -> a)[0]", + "src": "y.a", + "srcArrow": "none", + "dst": "y.a", + "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": 273.5, + "y": 40 + }, + { + "x": 269.34100341796875, + "y": 39.5620002746582 + }, + { + "x": 265.364990234375, + "y": 38.27000045776367 + }, + { + "x": 261.7439880371094, + "y": 36.18000030517578 + }, + { + "x": 258.6369934082031, + "y": 33.38199996948242 + }, + { + "x": 256.1789855957031, + "y": 30 + }, + { + "x": 254.47799682617188, + "y": 26.18000030517578 + }, + { + "x": 253.60899353027344, + "y": 22.09000015258789 + }, + { + "x": 253.60899353027344, + "y": 17.909000396728516 + }, + { + "x": 254.47799682617188, + "y": 13.819000244140625 + }, + { + "x": 256.1789855957031, + "y": 10 + }, + { + "x": 258.6369934082031, + "y": 6.617000102996826 + }, + { + "x": 261.7439880371094, + "y": 3.819000005722046 + }, + { + "x": 265.364990234375, + "y": 1.7289999723434448 + }, + { + "x": 269.34100341796875, + "y": 0.43700000643730164 + }, + { + "x": 273.5, + "y": 0 + }, + { + "x": 277.6579895019531, + "y": 0.43700000643730164 + }, + { + "x": 281.6340026855469, + "y": 1.7289999723434448 + }, + { + "x": 285.2550048828125, + "y": 3.819000005722046 + }, + { + "x": 288.36199951171875, + "y": 6.617000102996826 + }, + { + "x": 290.82000732421875, + "y": 10 + }, + { + "x": 292.52099609375, + "y": 13.819000244140625 + }, + { + "x": 293.3900146484375, + "y": 17.909000396728516 + }, + { + "x": 293.3900146484375, + "y": 22.09000015258789 + }, + { + "x": 292.52099609375, + "y": 26.18000030517578 + }, + { + "x": 290.82000732421875, + "y": 30 + }, + { + "x": 288.36199951171875, + "y": 33.38199996948242 + }, + { + "x": 285.2550048828125, + "y": 36.18000030517578 + }, + { + "x": 281.6340026855469, + "y": 38.27000045776367 + }, + { + "x": 277.6579895019531, + "y": 39.5620002746582 + }, + { + "x": 273.5, + "y": 40 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 } ], "root": { diff --git a/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg index fbfe5672ae..a31052ca40 100644 --- a/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg +++ b/e2etests/testdata/txtar/cycle-self-loop/tala/sketch.exp.svg @@ -1,9 +1,9 @@ -ab - + .d2-945278343 .fill-N1{fill:#0A0F25;} + .d2-945278343 .fill-N7{fill:#FFFFFF;} + .d2-945278343 .fill-B1{fill:#0D32B2;} + .d2-945278343 .fill-B5{fill:#EDF0FD;} + .d2-945278343 .stroke-B1{stroke:#0D32B2;} + .d2-945278343 .stroke-B2{stroke:#0D32B2;}.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;}.light-code{display: block}.dark-code{display: none}]]>aba + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index a726fb704b..0083b67cfa 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -1779,6 +1779,10 @@ x: "" { a -> a a -> b } +y: "" { + shape: cycle + a -> a +} -- cycle-diagram -- 1: "" { From 5d9406cdcd3a15e6e6db4bcbeff02f5b27a088e7 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Mon, 12 Oct 2026 11:12:44 +0900 Subject: [PATCH 17/17] e2etests: add isometric snapshots for the cycle txtar cases Upstream now renders every txtar case to isometric SVG and compares it against isometric.exp.svg. The two cycle cases render without external resources, so they get snapshots like the other renderable cases. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01USg33FuwdnSR33mSQEwbaU --- e2etests/testdata/txtar/cycle-diagram/dagre/isometric.exp.svg | 1 + e2etests/testdata/txtar/cycle-diagram/elk/isometric.exp.svg | 1 + e2etests/testdata/txtar/cycle-diagram/tala/isometric.exp.svg | 1 + e2etests/testdata/txtar/cycle-self-loop/dagre/isometric.exp.svg | 1 + e2etests/testdata/txtar/cycle-self-loop/elk/isometric.exp.svg | 1 + e2etests/testdata/txtar/cycle-self-loop/tala/isometric.exp.svg | 1 + 6 files changed, 6 insertions(+) create mode 100644 e2etests/testdata/txtar/cycle-diagram/dagre/isometric.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-diagram/elk/isometric.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-diagram/tala/isometric.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-self-loop/dagre/isometric.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-self-loop/elk/isometric.exp.svg create mode 100644 e2etests/testdata/txtar/cycle-self-loop/tala/isometric.exp.svg diff --git a/e2etests/testdata/txtar/cycle-diagram/dagre/isometric.exp.svg b/e2etests/testdata/txtar/cycle-diagram/dagre/isometric.exp.svg new file mode 100644 index 0000000000..1ac53d5781 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/dagre/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/elk/isometric.exp.svg b/e2etests/testdata/txtar/cycle-diagram/elk/isometric.exp.svg new file mode 100644 index 0000000000..96aef41957 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/elk/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-diagram/tala/isometric.exp.svg b/e2etests/testdata/txtar/cycle-diagram/tala/isometric.exp.svg new file mode 100644 index 0000000000..3898c33b80 --- /dev/null +++ b/e2etests/testdata/txtar/cycle-diagram/tala/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/dagre/isometric.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/dagre/isometric.exp.svg new file mode 100644 index 0000000000..823eae6d5b --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/dagre/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/elk/isometric.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/elk/isometric.exp.svg new file mode 100644 index 0000000000..7fb5db723c --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/elk/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file diff --git a/e2etests/testdata/txtar/cycle-self-loop/tala/isometric.exp.svg b/e2etests/testdata/txtar/cycle-self-loop/tala/isometric.exp.svg new file mode 100644 index 0000000000..1f96c315cc --- /dev/null +++ b/e2etests/testdata/txtar/cycle-self-loop/tala/isometric.exp.svg @@ -0,0 +1 @@ +D2 isometric diagram \ No newline at end of file