Merge branch 'terrastruct:master' into master
|
|
@ -228,6 +228,7 @@ let us know and we'll be happy to include it here!
|
|||
- **Mongo to D2**: [https://github.com/novuhq/mongo-to-D2](https://github.com/novuhq/mongo-to-D2)
|
||||
- **Pandoc filter**: [https://github.com/ram02z/d2-filter](https://github.com/ram02z/d2-filter)
|
||||
- **Logseq-D2**: [https://github.com/b-yp/logseq-d2](https://github.com/b-yp/logseq-d2)
|
||||
- **ent2d2**: [https://github.com/tmc/ent2d2](https://github.com/b-yp/logseq-d2)
|
||||
|
||||
### Misc
|
||||
|
||||
|
|
@ -275,5 +276,3 @@ this selected list of featured projects using D2.
|
|||
- Cloud service emulator (46k stars)
|
||||
- [Queue Library](https://github.com/golang-queue/queue/tree/master/images)
|
||||
- Queue is a Golang library for spawning and managing a Goroutine pool
|
||||
- [ent2d2](https://github.com/tmc/ent2d2)
|
||||
- A project to render entity relation (ER) diagrams for the [Ent](https://entgo.io) ORM.
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
#### Features 🚀
|
||||
|
||||
- `class` field now accepts arrays. See [docs](TODO). [#1256](https://github.com/terrastruct/d2/pull/1256)
|
||||
- Pill shape is implemented with rectangles of large border radius. Thanks @Poivey ! [#1006](https://github.com/terrastruct/d2/pull/1006)
|
||||
|
||||
#### Improvements 🧹
|
||||
|
||||
- ELK self loops get distributed around the object instead of stacking [#1232](https://github.com/terrastruct/d2/pull/1232)
|
||||
- ELK preserves order of objects in cycles [#1235](https://github.com/terrastruct/d2/pull/1235)
|
||||
- Improper usages of `class` and `style` get error messages [#1254](https://github.com/terrastruct/d2/pull/1254)
|
||||
- Improves scaling of object widths/heights in grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)
|
||||
- Enhance Markdown parsing error message by appending link to docs [#1269](https://github.com/terrastruct/d2/pull/1269)
|
||||
- Use shape specific sizing for grid containers [#1294](https://github.com/terrastruct/d2/pull/1294)
|
||||
- Grid diagrams now support nested shapes or grid diagrams [#1309](https://github.com/terrastruct/d2/pull/1309)
|
||||
- Grid diagrams will now also use `grid-gap`, `vertical-gap`, and `horizontal-gap` for padding [#1309](https://github.com/terrastruct/d2/pull/1309)
|
||||
- Watch mode browser uses an error favicon to easily indicate compiler errors. Thanks @sinyo-matu ! [#1240](https://github.com/terrastruct/d2/pull/1240)
|
||||
- Improves grid layout performance when there are many similarly sized shapes. [#1315](https://github.com/terrastruct/d2/pull/1315)
|
||||
- Connections and labels now are adjusted for shapes with `3d` or `multiple`. [#1340](https://github.com/terrastruct/d2/pull/1340)
|
||||
|
||||
#### Bugfixes ⛑️
|
||||
|
||||
- Fixes an issue with markdown labels that are empty when rendered [#1223](https://github.com/terrastruct/d2/issues/1223)
|
||||
- ELK self loops always have enough space for long labels [#1232](https://github.com/terrastruct/d2/pull/1232)
|
||||
- Fixes panic when setting `shape` to be `class` or `sql_table` within a class [#1251](https://github.com/terrastruct/d2/pull/1251)
|
||||
- Fixes rare panic exporting to gifs [#1257](https://github.com/terrastruct/d2/pull/1257)
|
||||
- Fixes bad performance in large grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)
|
||||
- Fixes bug in ELK when container has ID "root" [#1268](https://github.com/terrastruct/d2/pull/1268)
|
||||
- Fixes edge case panic with invalid CLI arguments [#1271](https://github.com/terrastruct/d2/pull/1271)
|
||||
- Shadow is cut off when `--pad` is 0. Thank you @LeonardsonCC ! [#1326](https://github.com/terrastruct/d2/pull/1326)
|
||||
- Fixes grid layout overwriting label placements for nested objects. [#1345](https://github.com/terrastruct/d2/pull/1345)
|
||||
- Fixes fonts not rendering correctly on certain platforms. Thanks @mikeday for identifying the solution. [#1356](https://github.com/terrastruct/d2/pull/1356)
|
||||
- Fixes folders not rendering in animations (`--animate-interval`) [#1357](https://github.com/terrastruct/d2/pull/1357)
|
||||
- Fixes panic using reserved keywords as containers [#1358](https://github.com/terrastruct/d2/pull/1358)
|
||||
- When multiple classes are applied changing different attributes of arrowheads, they are
|
||||
all applied instead of only the last one [#1362](https://github.com/terrastruct/d2/pull/1362)
|
||||
- Prevent empty block strings [#1364](https://github.com/terrastruct/d2/pull/1364)
|
||||
- Fixes dagre mis-aligning a nested shape's connection. [#1370](https://github.com/terrastruct/d2/pull/1370)
|
||||
|
|
|
|||
55
ci/release/changelogs/v0.4.2.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
This release improves on the features introduced in 0.4, with `class` keyword now accepting multiple class values with an array, and grid diagrams becoming faster and more robust.
|
||||
|
||||
Multiple classes example:
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/3120367/235749202-aa85830e-8f4a-4a2c-be16-599302919122.svg" style="width: 600px" />
|
||||
|
||||
```d2
|
||||
classes: {
|
||||
base: {
|
||||
style: {
|
||||
stroke-dash: 2
|
||||
border-radius: 5
|
||||
font: mono
|
||||
text-transform: uppercase
|
||||
}
|
||||
}
|
||||
error: {
|
||||
style.fill: "#e07d7d"
|
||||
style.stroke: "#a60c0c"
|
||||
style.font-color: white
|
||||
}
|
||||
success: {
|
||||
style.fill: "#86f499"
|
||||
style.stroke: "#017f07"
|
||||
style.font-color: black
|
||||
}
|
||||
}
|
||||
|
||||
server-1.class: [base; error]
|
||||
server-2.class: [base; success]
|
||||
|
||||
```
|
||||
|
||||
#### Features 🚀
|
||||
|
||||
- `class` field now accepts arrays. See [docs](https://d2lang.com/tour/classes/#multiple-classes). [#1256](https://github.com/terrastruct/d2/pull/1256)
|
||||
- Pill shape is implemented with rectangles of large border radius. See [docs](https://d2lang.com/tour/style/#border-radius). Thanks @Poivey ! [#1006](https://github.com/terrastruct/d2/pull/1006)
|
||||
|
||||
#### Improvements 🧹
|
||||
|
||||
- ELK self loops get distributed around the object instead of stacking [#1232](https://github.com/terrastruct/d2/pull/1232)
|
||||
- ELK preserves order of objects in cycles [#1235](https://github.com/terrastruct/d2/pull/1235)
|
||||
- Improper usages of `class` and `style` get error messages [#1254](https://github.com/terrastruct/d2/pull/1254)
|
||||
- Improves scaling of object widths/heights in grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)
|
||||
- Enhance Markdown parsing error message by appending link to docs [#1269](https://github.com/terrastruct/d2/pull/1269)
|
||||
|
||||
#### Bugfixes ⛑️
|
||||
|
||||
- Fixes an issue with markdown labels that are empty when rendered [#1223](https://github.com/terrastruct/d2/issues/1223)
|
||||
- ELK self loops always have enough space for long labels [#1232](https://github.com/terrastruct/d2/pull/1232)
|
||||
- Fixes panic when setting `shape` to be `class` or `sql_table` within a class [#1251](https://github.com/terrastruct/d2/pull/1251)
|
||||
- Fixes rare panic exporting to gifs [#1257](https://github.com/terrastruct/d2/pull/1257)
|
||||
- Fixes bad performance in large grid diagrams [#1263](https://github.com/terrastruct/d2/pull/1263)
|
||||
- Fixes bug in ELK when container has ID "root" [#1268](https://github.com/terrastruct/d2/pull/1268)
|
||||
- Fixes edge case panic with invalid CLI arguments [#1271](https://github.com/terrastruct/d2/pull/1271)
|
||||
|
|
@ -611,9 +611,13 @@ func (mk1 *Key) Equals(mk2 *Key) bool {
|
|||
return false
|
||||
}
|
||||
if (mk1.Value.Map == nil) != (mk2.Value.Map == nil) {
|
||||
return false
|
||||
}
|
||||
if (mk1.Value.Unbox() == nil) != (mk2.Value.Unbox() == nil) {
|
||||
if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
|
||||
return false
|
||||
}
|
||||
if mk2.Value.Map != nil && len(mk2.Value.Map.Nodes) > 0 {
|
||||
return false
|
||||
}
|
||||
} else if (mk1.Value.Unbox() == nil) != (mk2.Value.Unbox() == nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -638,7 +642,7 @@ func (mk1 *Key) Equals(mk2 *Key) bool {
|
|||
}
|
||||
}
|
||||
|
||||
if mk1.Value.Map != nil {
|
||||
if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
|
||||
if len(mk1.Value.Map.Nodes) != len(mk2.Value.Map.Nodes) {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -641,10 +641,9 @@ func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plug
|
|||
ms.Log.Success.Printf("successfully compiled %s to %s in %s", ms.HumanPath(inputPath), ms.HumanPath(boardOutputPath), dur)
|
||||
}
|
||||
boards = append([][]byte{out}, boards...)
|
||||
return boards, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return boards, nil
|
||||
}
|
||||
|
||||
func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
|
||||
|
|
|
|||
BIN
d2cli/static/favicon-err.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
d2cli/static/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -28,7 +28,7 @@ function init(reconnectDelay) {
|
|||
// we can't just set `d2SVG.innerHTML = msg.svg` need to parse this as xml not html
|
||||
const parsedXML = new DOMParser().parseFromString(msg.svg, "text/xml");
|
||||
d2SVG.replaceChildren(parsedXML.documentElement);
|
||||
|
||||
changeFavicon("./static/favicon.ico");
|
||||
const svgEl = d2SVG.querySelector("#d2-svg");
|
||||
// just use inner SVG in watch mode
|
||||
svgEl.parentElement.replaceWith(svgEl);
|
||||
|
|
@ -56,6 +56,7 @@ function init(reconnectDelay) {
|
|||
if (msg.err) {
|
||||
d2ErrDiv.innerText = msg.err;
|
||||
d2ErrDiv.style.display = "block";
|
||||
changeFavicon("./static/favicon-err.ico");
|
||||
d2ErrDiv.scrollIntoView();
|
||||
}
|
||||
};
|
||||
|
|
@ -73,3 +74,8 @@ function init(reconnectDelay) {
|
|||
}, reconnectDelay);
|
||||
};
|
||||
}
|
||||
|
||||
const changeFavicon = function (iconURL) {
|
||||
const faviconLink = document.getElementById("favicon");
|
||||
faviconLink.href = iconURL;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -429,6 +429,7 @@ func (w *watcher) handleRoot(hw http.ResponseWriter, r *http.Request) {
|
|||
<title>%s</title>
|
||||
<script src="./static/watch.js"></script>
|
||||
<link rel="stylesheet" href="./static/watch.css">
|
||||
<link id="favicon" rel="icon" href="./static/favicon.ico">
|
||||
</head>
|
||||
<body data-d2-dev-mode=%t>
|
||||
<div id="d2-err" style="display: none"></div>
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
|
|||
c.compileReserved(&obj.Attributes, f)
|
||||
return
|
||||
} else if f.Name == "style" {
|
||||
if f.Map() == nil {
|
||||
c.errorf(f.LastRef().AST(), `"style" expected to be set to a map, or contain an additional keyword like "style.opacity: 0.4"`)
|
||||
if f.Map() == nil || len(f.Map().Fields) == 0 {
|
||||
c.errorf(f.LastRef().AST(), `"style" expected to be set to a map of key-values, or contain an additional keyword like "style.opacity: 0.4"`)
|
||||
return
|
||||
}
|
||||
c.compileStyle(&obj.Attributes, f.Map())
|
||||
|
|
@ -270,6 +270,9 @@ func (c *compiler) compileLabel(attrs *d2graph.Attributes, f d2ir.Node) {
|
|||
// TODO: Delete instead.
|
||||
attrs.Label.Value = scalar.ScalarString()
|
||||
case *d2ast.BlockString:
|
||||
if strings.TrimSpace(scalar.ScalarString()) == "" {
|
||||
c.errorf(f.LastPrimaryKey(), "block string cannot be empty")
|
||||
}
|
||||
attrs.Language = scalar.Tag
|
||||
fullTag, ok := ShortToFullLanguageAliases[scalar.Tag]
|
||||
if ok {
|
||||
|
|
@ -675,10 +678,14 @@ func (c *compiler) compileEdgeField(edge *d2graph.Edge, f *d2ir.Field) {
|
|||
func (c *compiler) compileArrowheads(edge *d2graph.Edge, f *d2ir.Field) {
|
||||
var attrs *d2graph.Attributes
|
||||
if f.Name == "source-arrowhead" {
|
||||
edge.SrcArrowhead = &d2graph.Attributes{}
|
||||
if edge.SrcArrowhead == nil {
|
||||
edge.SrcArrowhead = &d2graph.Attributes{}
|
||||
}
|
||||
attrs = edge.SrcArrowhead
|
||||
} else {
|
||||
edge.DstArrowhead = &d2graph.Attributes{}
|
||||
if edge.DstArrowhead == nil {
|
||||
edge.DstArrowhead = &d2graph.Attributes{}
|
||||
}
|
||||
attrs = edge.DstArrowhead
|
||||
}
|
||||
|
||||
|
|
@ -847,13 +854,6 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) {
|
|||
if !in && arrowheadIn {
|
||||
c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid shape, can only set "%s" for arrowheads`, obj.Shape.Value))
|
||||
}
|
||||
case "grid-rows", "grid-columns", "grid-gap", "vertical-gap", "horizontal-gap":
|
||||
for _, child := range obj.ChildrenArray {
|
||||
if child.IsContainer() {
|
||||
c.errorf(f.LastPrimaryKey(),
|
||||
fmt.Sprintf(`%#v can only be used on containers with one level of nesting right now. (%#v has nested %#v)`, keyword, child.AbsID(), child.ChildrenArray[0].ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -610,6 +610,24 @@ x: {
|
|||
expErr: `d2/testdata/d2compiler/TestCompile/md_block_string_err.d2:4:19: unexpected text after md block string. See https://d2lang.com/tour/text#advanced-block-strings.
|
||||
d2/testdata/d2compiler/TestCompile/md_block_string_err.d2:5:1: block string must be terminated with |`,
|
||||
},
|
||||
{
|
||||
name: "no_empty_block_string",
|
||||
text: `Text: |md |`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/no_empty_block_string.d2:1:1: block string cannot be empty`,
|
||||
},
|
||||
{
|
||||
name: "no_white_spaces_only_block_string",
|
||||
text: `Text: |md |`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/no_white_spaces_only_block_string.d2:1:1: block string cannot be empty`,
|
||||
},
|
||||
{
|
||||
name: "no_new_lines_only_block_string",
|
||||
text: `Text: |md
|
||||
|
||||
|
||||
|`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/no_new_lines_only_block_string.d2:1:1: block string cannot be empty`,
|
||||
},
|
||||
{
|
||||
name: "underscore_edge_existing",
|
||||
|
||||
|
|
@ -1700,7 +1718,13 @@ x.a.b`,
|
|||
name: "tail-style",
|
||||
|
||||
text: `myobj.style: 3`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/tail-style.d2:1:7: "style" expected to be set to a map, or contain an additional keyword like "style.opacity: 0.4"`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/tail-style.d2:1:7: "style" expected to be set to a map of key-values, or contain an additional keyword like "style.opacity: 0.4"`,
|
||||
},
|
||||
{
|
||||
name: "tail-style-map",
|
||||
|
||||
text: `myobj.style: {}`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/tail-style-map.d2:1:7: "style" expected to be set to a map of key-values, or contain an additional keyword like "style.opacity: 0.4"`,
|
||||
},
|
||||
{
|
||||
name: "bad-style-nesting",
|
||||
|
|
@ -1716,6 +1740,13 @@ y -> x.style
|
|||
`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/edge_to_style.d2:2:8: reserved keywords are prohibited in edges`,
|
||||
},
|
||||
{
|
||||
name: "keyword-container",
|
||||
|
||||
text: `a.near.b
|
||||
`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/keyword-container.d2:1:3: "near" must be the last part of the key`,
|
||||
},
|
||||
{
|
||||
name: "escaped_id",
|
||||
|
||||
|
|
@ -2377,11 +2408,17 @@ d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edges in grid diagrams are
|
|||
a
|
||||
b
|
||||
c
|
||||
d.invalid descendant
|
||||
d.valid descendant
|
||||
e: {
|
||||
grid-rows: 1
|
||||
grid-columns: 2
|
||||
|
||||
a
|
||||
b
|
||||
}
|
||||
}
|
||||
`,
|
||||
expErr: `d2/testdata/d2compiler/TestCompile/grid_nested.d2:2:2: "grid-rows" can only be used on containers with one level of nesting right now. ("hey.d" has nested "invalid descendant")
|
||||
d2/testdata/d2compiler/TestCompile/grid_nested.d2:3:2: "grid-columns" can only be used on containers with one level of nesting right now. ("hey.d" has nested "invalid descendant")`,
|
||||
expErr: ``,
|
||||
},
|
||||
{
|
||||
name: "classes",
|
||||
|
|
@ -2460,6 +2497,29 @@ classes.x.shape: diamond
|
|||
tassert.Equal(t, "diamond", g.Objects[0].Shape.Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested-array-classes",
|
||||
text: `classes: {
|
||||
one target: {
|
||||
target-arrowhead.label: 1
|
||||
}
|
||||
association: {
|
||||
target-arrowhead.shape: arrow
|
||||
}
|
||||
}
|
||||
|
||||
a -> b: { class: [one target; association] }
|
||||
a -> b: { class: [association; one target] }
|
||||
`,
|
||||
assertions: func(t *testing.T, g *d2graph.Graph) {
|
||||
// They have the same, regardless of order of class application
|
||||
// since the classes modify attributes exclusive of each other
|
||||
tassert.Equal(t, "1", g.Edges[0].DstArrowhead.Label.Value)
|
||||
tassert.Equal(t, "1", g.Edges[1].DstArrowhead.Label.Value)
|
||||
tassert.Equal(t, "arrow", g.Edges[0].DstArrowhead.Shape.Value)
|
||||
tassert.Equal(t, "arrow", g.Edges[1].DstArrowhead.Shape.Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "class-shape-class",
|
||||
text: `classes: {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/d2themes"
|
||||
"oss.terrastruct.com/d2/lib/color"
|
||||
"oss.terrastruct.com/d2/lib/geo"
|
||||
)
|
||||
|
||||
func Export(ctx context.Context, g *d2graph.Graph, fontFamily *d2fonts.FontFamily) (*d2target.Diagram, error) {
|
||||
|
|
@ -295,9 +296,16 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
|
|||
connection.LabelPosition = *edge.LabelPosition
|
||||
}
|
||||
if edge.LabelPercentage != nil {
|
||||
connection.LabelPercentage = *edge.LabelPercentage
|
||||
connection.LabelPercentage = float64(float32(*edge.LabelPercentage))
|
||||
}
|
||||
connection.Route = edge.Route
|
||||
connection.Route = make([]*geo.Point, 0, len(edge.Route))
|
||||
for i := range edge.Route {
|
||||
p := edge.Route[i].Copy()
|
||||
p.TruncateDecimals()
|
||||
p.TruncateFloat32()
|
||||
connection.Route = append(connection.Route, p)
|
||||
}
|
||||
|
||||
connection.IsCurve = edge.IsCurve
|
||||
|
||||
connection.Src = edge.Src.AbsID()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package d2graph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -1054,6 +1055,45 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
|
|||
return &dims, nil
|
||||
}
|
||||
|
||||
// resizes the object to fit content of the given width and height in its inner box with the given padding.
|
||||
// this accounts for the shape of the object, and if there is a desired width or height set for the object
|
||||
func (obj *Object) SizeToContent(contentWidth, contentHeight, paddingX, paddingY float64) {
|
||||
var desiredWidth int
|
||||
var desiredHeight int
|
||||
if obj.WidthAttr != nil {
|
||||
desiredWidth, _ = strconv.Atoi(obj.WidthAttr.Value)
|
||||
}
|
||||
if obj.HeightAttr != nil {
|
||||
desiredHeight, _ = strconv.Atoi(obj.HeightAttr.Value)
|
||||
}
|
||||
|
||||
dslShape := strings.ToLower(obj.Shape.Value)
|
||||
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
|
||||
s := shape.NewShape(shapeType, geo.NewBox(geo.NewPoint(0, 0), contentWidth, contentHeight))
|
||||
|
||||
var fitWidth, fitHeight float64
|
||||
if shapeType == shape.PERSON_TYPE {
|
||||
fitWidth = contentWidth + paddingX
|
||||
fitHeight = contentHeight + paddingY
|
||||
} else {
|
||||
fitWidth, fitHeight = s.GetDimensionsToFit(contentWidth, contentHeight, paddingX, paddingY)
|
||||
}
|
||||
obj.Width = math.Max(float64(desiredWidth), fitWidth)
|
||||
obj.Height = math.Max(float64(desiredHeight), fitHeight)
|
||||
if s.AspectRatio1() {
|
||||
sideLength := math.Max(obj.Width, obj.Height)
|
||||
obj.Width = sideLength
|
||||
obj.Height = sideLength
|
||||
} else if desiredHeight == 0 || desiredWidth == 0 {
|
||||
switch s.GetType() {
|
||||
case shape.PERSON_TYPE:
|
||||
obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.PERSON_AR_LIMIT)
|
||||
case shape.OVAL_TYPE:
|
||||
obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.OVAL_AR_LIMIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *Object) OuterNearContainer() *Object {
|
||||
for obj != nil {
|
||||
if obj.NearKey != nil {
|
||||
|
|
@ -1437,7 +1477,6 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
|
|||
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(defaultDims.Width), float64(defaultDims.Height))
|
||||
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
|
||||
s := shape.NewShape(shapeType, contentBox)
|
||||
|
||||
paddingX, paddingY := s.GetDefaultPadding()
|
||||
if desiredWidth != 0 {
|
||||
paddingX = 0.
|
||||
|
|
@ -1470,27 +1509,7 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
|
|||
}
|
||||
}
|
||||
|
||||
var fitWidth, fitHeight float64
|
||||
if shapeType == shape.PERSON_TYPE {
|
||||
fitWidth = contentBox.Width + paddingX
|
||||
fitHeight = contentBox.Height + paddingY
|
||||
} else {
|
||||
fitWidth, fitHeight = s.GetDimensionsToFit(contentBox.Width, contentBox.Height, paddingX, paddingY)
|
||||
}
|
||||
obj.Width = math.Max(float64(desiredWidth), fitWidth)
|
||||
obj.Height = math.Max(float64(desiredHeight), fitHeight)
|
||||
if s.AspectRatio1() {
|
||||
sideLength := math.Max(obj.Width, obj.Height)
|
||||
obj.Width = sideLength
|
||||
obj.Height = sideLength
|
||||
} else if desiredHeight == 0 || desiredWidth == 0 {
|
||||
switch s.GetType() {
|
||||
case shape.PERSON_TYPE:
|
||||
obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.PERSON_AR_LIMIT)
|
||||
case shape.OVAL_TYPE:
|
||||
obj.Width, obj.Height = shape.LimitAR(obj.Width, obj.Height, shape.OVAL_AR_LIMIT)
|
||||
}
|
||||
}
|
||||
obj.SizeToContent(contentBox.Width, contentBox.Height, paddingX, paddingY)
|
||||
}
|
||||
for _, edge := range g.Edges {
|
||||
usedFont := fontFamily
|
||||
|
|
@ -1601,9 +1620,6 @@ func Key(k *d2ast.KeyPath) []string {
|
|||
// All reserved keywords. See init below.
|
||||
var ReservedKeywords map[string]struct{}
|
||||
|
||||
// All reserved keywords not including style keywords.
|
||||
var ReservedKeywords2 map[string]struct{}
|
||||
|
||||
// Non Style/Holder keywords.
|
||||
var SimpleReservedKeywords = map[string]struct{}{
|
||||
"label": {},
|
||||
|
|
@ -1625,16 +1641,20 @@ var SimpleReservedKeywords = map[string]struct{}{
|
|||
"vertical-gap": {},
|
||||
"horizontal-gap": {},
|
||||
"class": {},
|
||||
"classes": {},
|
||||
}
|
||||
|
||||
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and exist solely to hold a set of reserved keywords
|
||||
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and must hold composites
|
||||
var ReservedKeywordHolders = map[string]struct{}{
|
||||
"style": {},
|
||||
"source-arrowhead": {},
|
||||
"target-arrowhead": {},
|
||||
}
|
||||
|
||||
// CompositeReservedKeywords are reserved keywords that can hold composites
|
||||
var CompositeReservedKeywords = map[string]struct{}{
|
||||
"classes": {},
|
||||
}
|
||||
|
||||
// StyleKeywords are reserved keywords which cannot exist outside of the "style" keyword
|
||||
var StyleKeywords = map[string]struct{}{
|
||||
"opacity": {},
|
||||
|
|
@ -1708,23 +1728,15 @@ func init() {
|
|||
ReservedKeywords[k] = v
|
||||
}
|
||||
for k, v := range ReservedKeywordHolders {
|
||||
ReservedKeywords[k] = v
|
||||
CompositeReservedKeywords[k] = v
|
||||
}
|
||||
for k, v := range BoardKeywords {
|
||||
CompositeReservedKeywords[k] = v
|
||||
}
|
||||
for k, v := range CompositeReservedKeywords {
|
||||
ReservedKeywords[k] = v
|
||||
}
|
||||
|
||||
ReservedKeywords2 = make(map[string]struct{})
|
||||
for k, v := range SimpleReservedKeywords {
|
||||
ReservedKeywords2[k] = v
|
||||
}
|
||||
for k, v := range ReservedKeywordHolders {
|
||||
ReservedKeywords2[k] = v
|
||||
}
|
||||
for k, v := range BoardKeywords {
|
||||
ReservedKeywords2[k] = v
|
||||
}
|
||||
|
||||
NearConstants = make(map[string]struct{}, len(NearConstantsArray))
|
||||
for _, k := range NearConstantsArray {
|
||||
NearConstants[k] = struct{}{}
|
||||
|
|
@ -1799,3 +1811,28 @@ func (g *Graph) ApplyTheme(themeID int64) error {
|
|||
g.Theme = &theme
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graph) PrintString() string {
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprint(buf, "Objects: [")
|
||||
for _, obj := range g.Objects {
|
||||
fmt.Fprintf(buf, "%#v @(%v)", obj.AbsID(), obj.TopLeft.ToString())
|
||||
}
|
||||
fmt.Fprint(buf, "]")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (obj *Object) IterDescendants(apply func(parent, child *Object)) {
|
||||
for _, c := range obj.ChildrenArray {
|
||||
apply(obj, c)
|
||||
c.IterDescendants(apply)
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *Object) IsMultiple() bool {
|
||||
return obj.Style.Multiple != nil && obj.Style.Multiple.Value == "true"
|
||||
}
|
||||
|
||||
func (obj *Object) Is3D() bool {
|
||||
return obj.Style.ThreeDee != nil && obj.Style.ThreeDee.Value == "true"
|
||||
}
|
||||
|
|
|
|||
315
d2graph/layout.go
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
package d2graph
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/lib/geo"
|
||||
"oss.terrastruct.com/d2/lib/label"
|
||||
"oss.terrastruct.com/d2/lib/shape"
|
||||
)
|
||||
|
||||
func (obj *Object) MoveWithDescendants(dx, dy float64) {
|
||||
obj.TopLeft.X += dx
|
||||
obj.TopLeft.Y += dy
|
||||
for _, child := range obj.ChildrenArray {
|
||||
child.MoveWithDescendants(dx, dy)
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *Object) MoveWithDescendantsTo(x, y float64) {
|
||||
dx := x - obj.TopLeft.X
|
||||
dy := y - obj.TopLeft.Y
|
||||
obj.MoveWithDescendants(dx, dy)
|
||||
}
|
||||
|
||||
func (parent *Object) removeChild(child *Object) {
|
||||
delete(parent.Children, strings.ToLower(child.ID))
|
||||
for i := 0; i < len(parent.ChildrenArray); i++ {
|
||||
if parent.ChildrenArray[i] == child {
|
||||
parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove obj and all descendants from graph, as a new Graph
|
||||
func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph {
|
||||
descendantObjects, edges := pluckObjAndEdges(g, obj)
|
||||
|
||||
tempGraph := NewGraph()
|
||||
tempGraph.Root.ChildrenArray = []*Object{obj}
|
||||
tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
|
||||
|
||||
for _, descendantObj := range descendantObjects {
|
||||
descendantObj.Graph = tempGraph
|
||||
}
|
||||
tempGraph.Objects = descendantObjects
|
||||
tempGraph.Edges = edges
|
||||
|
||||
obj.Parent.removeChild(obj)
|
||||
obj.Parent = tempGraph.Root
|
||||
|
||||
return tempGraph
|
||||
}
|
||||
|
||||
func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) {
|
||||
for i := 0; i < len(g.Edges); i++ {
|
||||
edge := g.Edges[i]
|
||||
if edge.Src == obj || edge.Dst == obj {
|
||||
edges = append(edges, edge)
|
||||
g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(g.Objects); i++ {
|
||||
temp := g.Objects[i]
|
||||
if temp.AbsID() == obj.AbsID() {
|
||||
descendantsObjects = append(descendantsObjects, obj)
|
||||
g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
|
||||
for _, child := range obj.ChildrenArray {
|
||||
subObjects, subEdges := pluckObjAndEdges(g, child)
|
||||
descendantsObjects = append(descendantsObjects, subObjects...)
|
||||
edges = append(edges, subEdges...)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return descendantsObjects, edges
|
||||
}
|
||||
|
||||
func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) {
|
||||
obj := tempGraph.Root.ChildrenArray[0]
|
||||
obj.MoveWithDescendantsTo(0, 0)
|
||||
obj.Parent = parent
|
||||
for _, obj := range tempGraph.Objects {
|
||||
obj.Graph = g
|
||||
}
|
||||
g.Objects = append(g.Objects, tempGraph.Objects...)
|
||||
parent.Children[strings.ToLower(obj.ID)] = obj
|
||||
parent.ChildrenArray = append(parent.ChildrenArray, obj)
|
||||
g.Edges = append(g.Edges, tempGraph.Edges...)
|
||||
}
|
||||
|
||||
// ShiftDescendants moves Object's descendants (not including itself)
|
||||
// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant)
|
||||
func (obj *Object) ShiftDescendants(dx, dy float64) {
|
||||
// also need to shift edges of descendants that are shifted
|
||||
movedEdges := make(map[*Edge]struct{})
|
||||
for _, e := range obj.Graph.Edges {
|
||||
isSrcDesc := e.Src.IsDescendantOf(obj)
|
||||
isDstDesc := e.Dst.IsDescendantOf(obj)
|
||||
|
||||
if isSrcDesc && isDstDesc {
|
||||
movedEdges[e] = struct{}{}
|
||||
for _, p := range e.Route {
|
||||
p.X += dx
|
||||
p.Y += dy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.IterDescendants(func(_, curr *Object) {
|
||||
curr.TopLeft.X += dx
|
||||
curr.TopLeft.Y += dy
|
||||
for _, e := range obj.Graph.Edges {
|
||||
if _, ok := movedEdges[e]; ok {
|
||||
continue
|
||||
}
|
||||
isSrc := e.Src == curr
|
||||
isDst := e.Dst == curr
|
||||
|
||||
if isSrc && isDst {
|
||||
for _, p := range e.Route {
|
||||
p.X += dx
|
||||
p.Y += dy
|
||||
}
|
||||
} else if isSrc {
|
||||
if dx == 0 {
|
||||
e.ShiftStart(dy, false)
|
||||
} else if dy == 0 {
|
||||
e.ShiftStart(dx, true)
|
||||
} else {
|
||||
e.Route[0].X += dx
|
||||
e.Route[0].Y += dy
|
||||
}
|
||||
} else if isDst {
|
||||
if dx == 0 {
|
||||
e.ShiftEnd(dy, false)
|
||||
} else if dy == 0 {
|
||||
e.ShiftEnd(dx, true)
|
||||
} else {
|
||||
e.Route[len(e.Route)-1].X += dx
|
||||
e.Route[len(e.Route)-1].Y += dy
|
||||
}
|
||||
}
|
||||
|
||||
if isSrc || isDst {
|
||||
movedEdges[e] = struct{}{}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ShiftStart moves the starting point of the route by delta either horizontally or vertically
|
||||
// if subsequent points are in line with the movement, they will be removed (unless it is the last point)
|
||||
// start end
|
||||
// . ├────┼────┼───┼────┼───┤ before
|
||||
// . ├──dx──►
|
||||
// . ├──┼───┼────┼───┤ after
|
||||
func (edge *Edge) ShiftStart(delta float64, isHorizontal bool) {
|
||||
position := func(p *geo.Point) float64 {
|
||||
if isHorizontal {
|
||||
return p.X
|
||||
}
|
||||
return p.Y
|
||||
}
|
||||
|
||||
start := edge.Route[0]
|
||||
next := edge.Route[1]
|
||||
isIncreasing := position(start) < position(next)
|
||||
if isHorizontal {
|
||||
start.X += delta
|
||||
} else {
|
||||
start.Y += delta
|
||||
}
|
||||
|
||||
if isIncreasing == (delta < 0) {
|
||||
// nothing more to do when moving away from the next point
|
||||
return
|
||||
}
|
||||
|
||||
isAligned := func(p *geo.Point) bool {
|
||||
if isHorizontal {
|
||||
return p.Y == start.Y
|
||||
}
|
||||
return p.X == start.X
|
||||
}
|
||||
isPastStart := func(p *geo.Point) bool {
|
||||
if delta > 0 {
|
||||
return position(p) < position(start)
|
||||
} else {
|
||||
return position(p) > position(start)
|
||||
}
|
||||
}
|
||||
|
||||
needsRemoval := false
|
||||
toRemove := make([]bool, len(edge.Route))
|
||||
for i := 1; i < len(edge.Route)-1; i++ {
|
||||
if !isAligned(edge.Route[i]) {
|
||||
break
|
||||
}
|
||||
if isPastStart(edge.Route[i]) {
|
||||
toRemove[i] = true
|
||||
needsRemoval = true
|
||||
}
|
||||
}
|
||||
if needsRemoval {
|
||||
edge.Route = geo.RemovePoints(edge.Route, toRemove)
|
||||
}
|
||||
}
|
||||
|
||||
// ShiftEnd moves the ending point of the route by delta either horizontally or vertically
|
||||
// if prior points are in line with the movement, they will be removed (unless it is the first point)
|
||||
func (edge *Edge) ShiftEnd(delta float64, isHorizontal bool) {
|
||||
position := func(p *geo.Point) float64 {
|
||||
if isHorizontal {
|
||||
return p.X
|
||||
}
|
||||
return p.Y
|
||||
}
|
||||
|
||||
end := edge.Route[len(edge.Route)-1]
|
||||
prev := edge.Route[len(edge.Route)-2]
|
||||
isIncreasing := position(prev) < position(end)
|
||||
if isHorizontal {
|
||||
end.X += delta
|
||||
} else {
|
||||
end.Y += delta
|
||||
}
|
||||
|
||||
if isIncreasing == (delta > 0) {
|
||||
// nothing more to do when moving away from the next point
|
||||
return
|
||||
}
|
||||
|
||||
isAligned := func(p *geo.Point) bool {
|
||||
if isHorizontal {
|
||||
return p.Y == end.Y
|
||||
}
|
||||
return p.X == end.X
|
||||
}
|
||||
isPastEnd := func(p *geo.Point) bool {
|
||||
if delta > 0 {
|
||||
return position(p) < position(end)
|
||||
} else {
|
||||
return position(p) > position(end)
|
||||
}
|
||||
}
|
||||
|
||||
needsRemoval := false
|
||||
toRemove := make([]bool, len(edge.Route))
|
||||
for i := len(edge.Route) - 2; i > 0; i-- {
|
||||
if !isAligned(edge.Route[i]) {
|
||||
break
|
||||
}
|
||||
if isPastEnd(edge.Route[i]) {
|
||||
toRemove[i] = true
|
||||
needsRemoval = true
|
||||
}
|
||||
}
|
||||
if needsRemoval {
|
||||
edge.Route = geo.RemovePoints(edge.Route, toRemove)
|
||||
}
|
||||
}
|
||||
|
||||
// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple
|
||||
func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) {
|
||||
if obj.Is3D() {
|
||||
if obj.Shape.Value == d2target.ShapeHexagon {
|
||||
dy = d2target.THREE_DEE_OFFSET / 2
|
||||
} else {
|
||||
dy = d2target.THREE_DEE_OFFSET
|
||||
}
|
||||
dx = d2target.THREE_DEE_OFFSET
|
||||
} else if obj.IsMultiple() {
|
||||
dy = d2target.MULTIPLE_OFFSET
|
||||
dx = d2target.MULTIPLE_OFFSET
|
||||
}
|
||||
return dx, dy
|
||||
}
|
||||
|
||||
func (obj *Object) ToShape() shape.Shape {
|
||||
tl := obj.TopLeft
|
||||
if tl == nil {
|
||||
tl = geo.NewPoint(0, 0)
|
||||
}
|
||||
dslShape := strings.ToLower(obj.Shape.Value)
|
||||
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
|
||||
contentBox := geo.NewBox(tl, obj.Width, obj.Height)
|
||||
return shape.NewShape(shapeType, contentBox)
|
||||
}
|
||||
|
||||
func (obj *Object) GetLabelTopLeft() *geo.Point {
|
||||
if obj.LabelPosition == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := obj.ToShape()
|
||||
labelPosition := label.Position(*obj.LabelPosition)
|
||||
|
||||
var box *geo.Box
|
||||
if labelPosition.IsOutside() {
|
||||
box = s.GetBox()
|
||||
} else {
|
||||
box = s.GetInnerBox()
|
||||
}
|
||||
|
||||
labelTL := labelPosition.GetPointOnBox(box, label.PADDING,
|
||||
float64(obj.LabelDimensions.Width),
|
||||
float64(obj.LabelDimensions.Height),
|
||||
)
|
||||
return labelTL
|
||||
}
|
||||
|
|
@ -669,10 +669,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field,
|
|||
|
||||
if _, ok := d2graph.ReservedKeywords[strings.ToLower(head)]; ok {
|
||||
head = strings.ToLower(head)
|
||||
}
|
||||
|
||||
if head == "class" && i < len(kp.Path)-1 {
|
||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), `"class" must be the last part of the key`)
|
||||
if _, ok := d2graph.CompositeReservedKeywords[head]; !ok && i < len(kp.Path)-1 {
|
||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), fmt.Sprintf(`"%s" must be the last part of the key`, head))
|
||||
}
|
||||
}
|
||||
|
||||
if head == "_" {
|
||||
|
|
|
|||
|
|
@ -119,9 +119,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
}
|
||||
|
||||
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
|
||||
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height))
|
||||
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
|
||||
s := shape.NewShape(shapeType, contentBox)
|
||||
s := obj.ToShape()
|
||||
iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft))
|
||||
// Since dagre container labels are pushed up, we don't want a child container to collide
|
||||
maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, (iconSize+label.PADDING*2)*2)
|
||||
|
|
@ -159,7 +157,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
id := obj.AbsID()
|
||||
idToObj[id] = obj
|
||||
|
||||
height := obj.Height
|
||||
width, height := obj.Width, obj.Height
|
||||
if obj.HasLabel() {
|
||||
if obj.HasOutsideBottomLabel() || obj.Icon != nil {
|
||||
height += float64(obj.LabelDimensions.Height) + label.PADDING
|
||||
|
|
@ -168,7 +166,12 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
height += float64(obj.LabelDimensions.Height) + label.PADDING
|
||||
}
|
||||
}
|
||||
loadScript += generateAddNodeLine(id, int(obj.Width), int(height))
|
||||
// reserve extra space for 3d/multiple by providing dagre the larger dimensions
|
||||
dx, dy := obj.GetModifierElementAdjustments()
|
||||
width += dx
|
||||
height += dy
|
||||
|
||||
loadScript += generateAddNodeLine(id, int(width), int(height))
|
||||
if obj.Parent != g.Root {
|
||||
loadScript += generateAddParentLine(id, obj.Parent.AbsID())
|
||||
}
|
||||
|
|
@ -232,10 +235,10 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
|
||||
// dagre gives center of node
|
||||
obj.TopLeft = geo.NewPoint(math.Round(dn.X-dn.Width/2), math.Round(dn.Y-dn.Height/2))
|
||||
obj.Width = dn.Width
|
||||
obj.Height = dn.Height
|
||||
obj.Width = math.Ceil(dn.Width)
|
||||
obj.Height = math.Ceil(dn.Height)
|
||||
|
||||
if obj.HasLabel() {
|
||||
if obj.HasLabel() && obj.LabelPosition == nil {
|
||||
if len(obj.ChildrenArray) > 0 {
|
||||
obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
|
||||
} else if obj.HasOutsideBottomLabel() {
|
||||
|
|
@ -248,7 +251,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
}
|
||||
if obj.Icon != nil {
|
||||
if obj.Icon != nil && obj.IconPosition == nil {
|
||||
if len(obj.ChildrenArray) > 0 {
|
||||
obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
|
||||
obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
|
||||
|
|
@ -381,7 +384,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
if isHorizontal && e.Src.Parent != g.Root && e.Dst.Parent != g.Root {
|
||||
moveWholeEdge = true
|
||||
} else {
|
||||
e.Route[0].Y += stepSize
|
||||
e.ShiftStart(stepSize, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -390,7 +393,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
if isHorizontal && e.Dst.Parent != g.Root && e.Src.Parent != g.Root {
|
||||
moveWholeEdge = true
|
||||
} else {
|
||||
e.Route[len(e.Route)-1].Y += stepSize
|
||||
e.ShiftEnd(stepSize, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -407,6 +410,20 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
}
|
||||
}
|
||||
|
||||
// remove the extra width/height we added for 3d/multiple after all objects/connections are placed
|
||||
// and shift the shapes down accordingly
|
||||
for _, obj := range g.Objects {
|
||||
dx, dy := obj.GetModifierElementAdjustments()
|
||||
if dx != 0 || dy != 0 {
|
||||
obj.TopLeft.Y += dy
|
||||
obj.ShiftDescendants(0, dy)
|
||||
if !obj.IsContainer() {
|
||||
obj.Width -= dx
|
||||
obj.Height -= dy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, edge := range g.Edges {
|
||||
points := edge.Route
|
||||
startIndex, endIndex := 0, len(points)-1
|
||||
|
|
@ -453,8 +470,27 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
}
|
||||
}
|
||||
|
||||
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
|
||||
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
|
||||
var originalSrcTL, originalDstTL *geo.Point
|
||||
// if the edge passes through 3d/multiple, use the offset box for tracing to border
|
||||
if srcDx, srcDy := edge.Src.GetModifierElementAdjustments(); srcDx != 0 || srcDy != 0 {
|
||||
if start.X > edge.Src.TopLeft.X+srcDx &&
|
||||
start.Y < edge.Src.TopLeft.Y+edge.Src.Height-srcDy {
|
||||
originalSrcTL = edge.Src.TopLeft.Copy()
|
||||
edge.Src.TopLeft.X += srcDx
|
||||
edge.Src.TopLeft.Y -= srcDy
|
||||
}
|
||||
}
|
||||
if dstDx, dstDy := edge.Dst.GetModifierElementAdjustments(); dstDx != 0 || dstDy != 0 {
|
||||
if end.X > edge.Dst.TopLeft.X+dstDx &&
|
||||
end.Y < edge.Dst.TopLeft.Y+edge.Dst.Height-dstDy {
|
||||
originalDstTL = edge.Dst.TopLeft.Copy()
|
||||
edge.Dst.TopLeft.X += dstDx
|
||||
edge.Dst.TopLeft.Y -= dstDy
|
||||
}
|
||||
}
|
||||
|
||||
srcShape := edge.Src.ToShape()
|
||||
dstShape := edge.Dst.ToShape()
|
||||
|
||||
// trace the edge to the specific shape's border
|
||||
points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1])
|
||||
|
|
@ -517,6 +553,16 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
if edge.Label.Value != "" {
|
||||
edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
|
||||
// undo 3d/multiple offset
|
||||
if originalSrcTL != nil {
|
||||
edge.Src.TopLeft.X = originalSrcTL.X
|
||||
edge.Src.TopLeft.Y = originalSrcTL.Y
|
||||
}
|
||||
if originalDstTL != nil {
|
||||
edge.Dst.TopLeft.X = originalDstTL.X
|
||||
edge.Dst.TopLeft.Y = originalDstTL.Y
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -222,6 +222,10 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
}
|
||||
width = go2.Max(width, float64(obj.LabelDimensions.Width))
|
||||
}
|
||||
// reserve extra space for 3d/multiple by providing elk the larger dimensions
|
||||
dx, dy := obj.GetModifierElementAdjustments()
|
||||
width += dx
|
||||
height += dy
|
||||
|
||||
n := &ELKNode{
|
||||
ID: obj.AbsID(),
|
||||
|
|
@ -400,10 +404,10 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
parentY = parent.TopLeft.Y
|
||||
}
|
||||
obj.TopLeft = geo.NewPoint(parentX+n.X, parentY+n.Y)
|
||||
obj.Width = n.Width
|
||||
obj.Height = n.Height
|
||||
obj.Width = math.Ceil(n.Width)
|
||||
obj.Height = math.Ceil(n.Height)
|
||||
|
||||
if obj.HasLabel() {
|
||||
if obj.HasLabel() && obj.LabelPosition == nil {
|
||||
if len(obj.ChildrenArray) > 0 {
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
} else if obj.HasOutsideBottomLabel() {
|
||||
|
|
@ -415,7 +419,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
}
|
||||
if obj.Icon != nil {
|
||||
if obj.Icon != nil && obj.IconPosition == nil {
|
||||
if len(obj.ChildrenArray) > 0 {
|
||||
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
|
||||
|
|
@ -454,10 +458,51 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
Y: parentY + s.End.Y,
|
||||
})
|
||||
}
|
||||
edge.Route = points
|
||||
}
|
||||
|
||||
// remove the extra width/height we added for 3d/multiple after all objects/connections are placed
|
||||
// and shift the shapes down accordingly
|
||||
for _, obj := range g.Objects {
|
||||
dx, dy := obj.GetModifierElementAdjustments()
|
||||
if dx != 0 || dy != 0 {
|
||||
obj.TopLeft.Y += dy
|
||||
obj.ShiftDescendants(0, dy)
|
||||
if !obj.IsContainer() {
|
||||
obj.Width -= dx
|
||||
obj.Height -= dy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, edge := range g.Edges {
|
||||
points := edge.Route
|
||||
|
||||
startIndex, endIndex := 0, len(points)-1
|
||||
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
|
||||
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
|
||||
start := points[startIndex]
|
||||
end := points[endIndex]
|
||||
|
||||
var originalSrcTL, originalDstTL *geo.Point
|
||||
// if the edge passes through 3d/multiple, use the offset box for tracing to border
|
||||
if srcDx, srcDy := edge.Src.GetModifierElementAdjustments(); srcDx != 0 || srcDy != 0 {
|
||||
if start.X > edge.Src.TopLeft.X+srcDx &&
|
||||
start.Y < edge.Src.TopLeft.Y+edge.Src.Height-srcDy {
|
||||
originalSrcTL = edge.Src.TopLeft.Copy()
|
||||
edge.Src.TopLeft.X += srcDx
|
||||
edge.Src.TopLeft.Y -= srcDy
|
||||
}
|
||||
}
|
||||
if dstDx, dstDy := edge.Dst.GetModifierElementAdjustments(); dstDx != 0 || dstDy != 0 {
|
||||
if end.X > edge.Dst.TopLeft.X+dstDx &&
|
||||
end.Y < edge.Dst.TopLeft.Y+edge.Dst.Height-dstDy {
|
||||
originalDstTL = edge.Dst.TopLeft.Copy()
|
||||
edge.Dst.TopLeft.X += dstDx
|
||||
edge.Dst.TopLeft.Y -= dstDy
|
||||
}
|
||||
}
|
||||
|
||||
srcShape := edge.Src.ToShape()
|
||||
dstShape := edge.Dst.ToShape()
|
||||
|
||||
// trace the edge to the specific shape's border
|
||||
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])
|
||||
|
|
@ -468,6 +513,16 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
|||
}
|
||||
|
||||
edge.Route = points
|
||||
|
||||
// undo 3d/multiple offset
|
||||
if originalSrcTL != nil {
|
||||
edge.Src.TopLeft.X = originalSrcTL.X
|
||||
edge.Src.TopLeft.Y = originalSrcTL.Y
|
||||
}
|
||||
if originalDstTL != nil {
|
||||
edge.Dst.TopLeft.X = originalDstTL.X
|
||||
edge.Dst.TopLeft.Y = originalDstTL.Y
|
||||
}
|
||||
}
|
||||
|
||||
deleteBends(g)
|
||||
|
|
@ -506,10 +561,11 @@ func deleteBends(g *d2graph.Graph) {
|
|||
}
|
||||
|
||||
isHorizontal := math.Ceil(start.Y) == math.Ceil(corner.Y)
|
||||
dx, dy := endpoint.GetModifierElementAdjustments()
|
||||
|
||||
// Make sure it's still attached
|
||||
if isHorizontal {
|
||||
if end.Y <= endpoint.TopLeft.Y+10 {
|
||||
if end.Y <= endpoint.TopLeft.Y+10-dy {
|
||||
continue
|
||||
}
|
||||
if end.Y >= endpoint.TopLeft.Y+endpoint.Height-10 {
|
||||
|
|
@ -519,7 +575,7 @@ func deleteBends(g *d2graph.Graph) {
|
|||
if end.X <= endpoint.TopLeft.X+10 {
|
||||
continue
|
||||
}
|
||||
if end.X >= endpoint.TopLeft.X+endpoint.Width-10 {
|
||||
if end.X >= endpoint.TopLeft.X+endpoint.Width-10+dx {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
d2layouts/d2grid/constants.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package d2grid
|
||||
|
||||
const (
|
||||
// don't consider layouts with rows longer than targetSize*1.2 or shorter than targetSize/1.2
|
||||
STARTING_THRESHOLD = 1.2
|
||||
// next try layouts with a 25% larger threshold
|
||||
THRESHOLD_STEP_SIZE = 0.25
|
||||
MIN_THRESHOLD_ATTEMPTS = 1
|
||||
MAX_THRESHOLD_ATTEMPTS = 3
|
||||
|
||||
ATTEMPT_LIMIT = 100_000
|
||||
SKIP_LIMIT = 10_000_000
|
||||
)
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/lib/geo"
|
||||
)
|
||||
|
||||
type gridDiagram struct {
|
||||
|
|
@ -95,22 +96,30 @@ func newGridDiagram(root *d2graph.Object) *gridDiagram {
|
|||
gd.horizontalGap, _ = strconv.Atoi(root.HorizontalGap.Value)
|
||||
}
|
||||
|
||||
for _, o := range gd.objects {
|
||||
o.TopLeft = geo.NewPoint(0, 0)
|
||||
}
|
||||
|
||||
return &gd
|
||||
}
|
||||
|
||||
func (gd *gridDiagram) shift(dx, dy float64) {
|
||||
for _, obj := range gd.objects {
|
||||
obj.TopLeft.X += dx
|
||||
obj.TopLeft.Y += dy
|
||||
obj.MoveWithDescendants(dx, dy)
|
||||
}
|
||||
}
|
||||
|
||||
func (gd *gridDiagram) cleanup(obj *d2graph.Object, graph *d2graph.Graph) {
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = make([]*d2graph.Object, 0)
|
||||
for _, child := range gd.objects {
|
||||
obj.Children[strings.ToLower(child.ID)] = child
|
||||
obj.ChildrenArray = append(obj.ChildrenArray, child)
|
||||
|
||||
restore := func(parent, child *d2graph.Object) {
|
||||
parent.Children[strings.ToLower(child.ID)] = child
|
||||
parent.ChildrenArray = append(parent.ChildrenArray, child)
|
||||
graph.Objects = append(graph.Objects, child)
|
||||
}
|
||||
for _, child := range gd.objects {
|
||||
restore(obj, child)
|
||||
child.IterDescendants(restore)
|
||||
}
|
||||
graph.Objects = append(graph.Objects, gd.objects...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package d2grid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/lib/geo"
|
||||
"oss.terrastruct.com/d2/lib/label"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
|
|
@ -29,7 +31,7 @@ const (
|
|||
// 7. Put grid children back in correct location
|
||||
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d2graph.LayoutGraph {
|
||||
return func(ctx context.Context, g *d2graph.Graph) error {
|
||||
gridDiagrams, objectOrder, err := withoutGridDiagrams(ctx, g)
|
||||
gridDiagrams, objectOrder, err := withoutGridDiagrams(ctx, g, layout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -45,10 +47,162 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d
|
|||
}
|
||||
}
|
||||
|
||||
func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph) (gridDiagrams map[string]*gridDiagram, objectOrder map[string]int, err error) {
|
||||
func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) (gridDiagrams map[string]*gridDiagram, objectOrder map[string]int, err error) {
|
||||
toRemove := make(map[*d2graph.Object]struct{})
|
||||
gridDiagrams = make(map[string]*gridDiagram)
|
||||
|
||||
objectOrder = make(map[string]int)
|
||||
for i, obj := range g.Objects {
|
||||
objectOrder[obj.AbsID()] = i
|
||||
}
|
||||
|
||||
var processGrid func(obj *d2graph.Object) error
|
||||
processGrid = func(obj *d2graph.Object) error {
|
||||
for _, child := range obj.ChildrenArray {
|
||||
if child.IsGridDiagram() {
|
||||
if err := processGrid(child); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if len(child.ChildrenArray) > 0 {
|
||||
tempGraph := g.ExtractAsNestedGraph(child)
|
||||
if err := layout(ctx, tempGraph); err != nil {
|
||||
return err
|
||||
}
|
||||
g.InjectNestedGraph(tempGraph, obj)
|
||||
|
||||
sort.SliceStable(g.Objects, func(i, j int) bool {
|
||||
return objectOrder[g.Objects[i].AbsID()] < objectOrder[g.Objects[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(child.ChildrenArray, func(i, j int) bool {
|
||||
return objectOrder[child.ChildrenArray[i].AbsID()] < objectOrder[child.ChildrenArray[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(obj.ChildrenArray, func(i, j int) bool {
|
||||
return objectOrder[obj.ChildrenArray[i].AbsID()] < objectOrder[obj.ChildrenArray[j].AbsID()]
|
||||
})
|
||||
|
||||
for _, o := range tempGraph.Objects {
|
||||
toRemove[o] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gd, err := layoutGrid(g, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = nil
|
||||
|
||||
if obj.Box != nil {
|
||||
// CONTAINER_PADDING is default, but use gap value if set
|
||||
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
|
||||
if obj.GridGap != nil || obj.HorizontalGap != nil {
|
||||
horizontalPadding = gd.horizontalGap
|
||||
}
|
||||
if obj.GridGap != nil || obj.VerticalGap != nil {
|
||||
verticalPadding = gd.verticalGap
|
||||
}
|
||||
|
||||
// size shape according to grid
|
||||
obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))
|
||||
|
||||
// compute where the grid should be placed inside shape
|
||||
s := obj.ToShape()
|
||||
innerBox := s.GetInnerBox()
|
||||
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
|
||||
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
|
||||
}
|
||||
|
||||
// compute how much space the label and icon occupy
|
||||
var occupiedWidth, occupiedHeight float64
|
||||
if obj.Icon != nil {
|
||||
iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
|
||||
occupiedWidth = iconSpace
|
||||
occupiedHeight = iconSpace
|
||||
}
|
||||
|
||||
var dx, dy float64
|
||||
if obj.LabelDimensions.Height != 0 {
|
||||
occupiedHeight = math.Max(
|
||||
occupiedHeight,
|
||||
float64(obj.LabelDimensions.Height)+2*label.PADDING,
|
||||
)
|
||||
}
|
||||
if obj.LabelDimensions.Width != 0 {
|
||||
// . ├────┤───────├────┤
|
||||
// . icon label icon
|
||||
// with an icon in top left we need 2x the space to fit the label in the center
|
||||
occupiedWidth *= 2
|
||||
occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING
|
||||
if occupiedWidth > obj.Width {
|
||||
dx = (occupiedWidth - obj.Width) / 2
|
||||
obj.Width = occupiedWidth
|
||||
}
|
||||
}
|
||||
|
||||
// also check for grid cells with outside top labels or icons
|
||||
// the first grid object is at the top (and always exists)
|
||||
topY := gd.objects[0].TopLeft.Y
|
||||
highestOutside := topY
|
||||
for _, o := range gd.objects {
|
||||
// we only want to compute label positions for objects at the top of the grid
|
||||
if o.TopLeft.Y > topY {
|
||||
if gd.rowDirected {
|
||||
// if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row
|
||||
break
|
||||
} else {
|
||||
// otherwise we continue until the next column
|
||||
continue
|
||||
}
|
||||
}
|
||||
if o.LabelPosition != nil {
|
||||
labelPosition := label.Position(*o.LabelPosition)
|
||||
if labelPosition.IsOutside() {
|
||||
labelTL := o.GetLabelTopLeft()
|
||||
if labelTL.Y < highestOutside {
|
||||
highestOutside = labelTL.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.IconPosition != nil {
|
||||
switch label.Position(*o.IconPosition) {
|
||||
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
|
||||
iconSpace := float64(d2target.MAX_ICON_SIZE + label.PADDING)
|
||||
if topY-iconSpace < highestOutside {
|
||||
highestOutside = topY - iconSpace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if highestOutside < topY {
|
||||
occupiedHeight += topY - highestOutside + 2*label.PADDING
|
||||
}
|
||||
if occupiedHeight > float64(verticalPadding) {
|
||||
// if the label doesn't fit within the padding, we need to add more
|
||||
dy = occupiedHeight - float64(verticalPadding)
|
||||
obj.Height += dy
|
||||
}
|
||||
|
||||
// we need to center children if we have to expand to fit the container label
|
||||
if dx != 0 || dy != 0 {
|
||||
gd.shift(dx, dy)
|
||||
}
|
||||
}
|
||||
|
||||
if obj.HasLabel() {
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
}
|
||||
if obj.Icon != nil {
|
||||
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
|
||||
}
|
||||
gridDiagrams[obj.AbsID()] = gd
|
||||
|
||||
for _, o := range gd.objects {
|
||||
toRemove[o] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(g.Objects) > 0 {
|
||||
queue := make([]*d2graph.Object, 1, len(g.Objects))
|
||||
queue[0] = g.Root
|
||||
|
|
@ -63,47 +217,14 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph) (gridDiagrams ma
|
|||
continue
|
||||
}
|
||||
|
||||
gd, err := layoutGrid(g, obj)
|
||||
if err != nil {
|
||||
if err := processGrid(obj); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = nil
|
||||
|
||||
var dx, dy float64
|
||||
width := gd.width + 2*CONTAINER_PADDING
|
||||
labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING
|
||||
if labelWidth > width {
|
||||
dx = (labelWidth - width) / 2
|
||||
width = labelWidth
|
||||
}
|
||||
height := gd.height + 2*CONTAINER_PADDING
|
||||
labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING
|
||||
if labelHeight > CONTAINER_PADDING {
|
||||
// if the label doesn't fit within the padding, we need to add more
|
||||
grow := labelHeight - CONTAINER_PADDING
|
||||
dy = grow / 2
|
||||
height += grow
|
||||
}
|
||||
// we need to center children if we have to expand to fit the container label
|
||||
if dx != 0 || dy != 0 {
|
||||
gd.shift(dx, dy)
|
||||
}
|
||||
obj.Box = geo.NewBox(nil, width, height)
|
||||
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
gridDiagrams[obj.AbsID()] = gd
|
||||
|
||||
for _, o := range gd.objects {
|
||||
toRemove[o] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
objectOrder = make(map[string]int)
|
||||
layoutObjects := make([]*d2graph.Object, 0, len(toRemove))
|
||||
for i, obj := range g.Objects {
|
||||
objectOrder[obj.AbsID()] = i
|
||||
for _, obj := range g.Objects {
|
||||
if _, exists := toRemove[obj]; !exists {
|
||||
layoutObjects = append(layoutObjects, obj)
|
||||
}
|
||||
|
|
@ -125,10 +246,17 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
|
|||
// position labels and icons
|
||||
for _, o := range gd.objects {
|
||||
if o.Icon != nil {
|
||||
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
// don't overwrite position if nested graph layout positioned label/icon
|
||||
if o.LabelPosition == nil {
|
||||
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
}
|
||||
if o.IconPosition == nil {
|
||||
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
} else {
|
||||
o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
if o.LabelPosition == nil {
|
||||
o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +319,7 @@ func (gd *gridDiagram) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
}
|
||||
o.Width = colWidths[j]
|
||||
o.Height = rowHeights[i]
|
||||
o.TopLeft = cursor.Copy()
|
||||
o.MoveWithDescendantsTo(cursor.X, cursor.Y)
|
||||
cursor.X += o.Width + horizontalGap
|
||||
}
|
||||
cursor.X = 0
|
||||
|
|
@ -206,7 +334,7 @@ func (gd *gridDiagram) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
}
|
||||
o.Width = colWidths[j]
|
||||
o.Height = rowHeights[i]
|
||||
o.TopLeft = cursor.Copy()
|
||||
o.MoveWithDescendantsTo(cursor.X, cursor.Y)
|
||||
cursor.Y += o.Height + verticalGap
|
||||
}
|
||||
cursor.X += colWidths[j] + horizontalGap
|
||||
|
|
@ -279,6 +407,8 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
maxX = math.Max(maxX, rowWidth)
|
||||
}
|
||||
|
||||
// TODO if object is a nested grid, consider growing descendants according to the inner grid layout
|
||||
|
||||
// then expand thinnest objects to make each row the same width
|
||||
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
|
||||
// . │ │ │ │ │ │ │
|
||||
|
|
@ -342,7 +472,7 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
for _, row := range layout {
|
||||
rowHeight := 0.
|
||||
for _, o := range row {
|
||||
o.TopLeft = cursor.Copy()
|
||||
o.MoveWithDescendantsTo(cursor.X, cursor.Y)
|
||||
cursor.X += o.Width + horizontalGap
|
||||
rowHeight = math.Max(rowHeight, o.Height)
|
||||
}
|
||||
|
|
@ -430,7 +560,7 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
for _, column := range layout {
|
||||
colWidth := 0.
|
||||
for _, o := range column {
|
||||
o.TopLeft = cursor.Copy()
|
||||
o.MoveWithDescendantsTo(cursor.X, cursor.Y)
|
||||
cursor.Y += o.Height + verticalGap
|
||||
colWidth = math.Max(colWidth, o.Width)
|
||||
}
|
||||
|
|
@ -452,6 +582,7 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
|
|||
// generate the best layout of objects aiming for each row to be the targetSize width
|
||||
// if columns is true, each column aims to have the targetSize height
|
||||
func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2graph.Object {
|
||||
debug := false
|
||||
var nCuts int
|
||||
if columns {
|
||||
nCuts = gd.columns - 1
|
||||
|
|
@ -462,6 +593,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
return genLayout(gd.objects, nil)
|
||||
}
|
||||
|
||||
var bestLayout [][]*d2graph.Object
|
||||
bestDist := math.MaxFloat64
|
||||
fastIsBest := false
|
||||
// try fast layout algorithm as a baseline
|
||||
if fastLayout := gd.fastLayout(targetSize, nCuts, columns); fastLayout != nil {
|
||||
dist := getDistToTarget(fastLayout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||
if debug {
|
||||
fmt.Printf("fast dist %v dist per row %v\n", dist, dist/(float64(nCuts)+1))
|
||||
}
|
||||
if dist == 0 {
|
||||
return fastLayout
|
||||
}
|
||||
bestDist = dist
|
||||
bestLayout = fastLayout
|
||||
fastIsBest = true
|
||||
}
|
||||
|
||||
var gap float64
|
||||
if columns {
|
||||
gap = float64(gd.verticalGap)
|
||||
|
|
@ -476,17 +624,24 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
}
|
||||
}
|
||||
|
||||
debug := false
|
||||
sizes := []float64{}
|
||||
for _, obj := range gd.objects {
|
||||
size := getSize(obj)
|
||||
sizes = append(sizes, size)
|
||||
}
|
||||
sd := stddev(sizes)
|
||||
if debug {
|
||||
fmt.Printf("sizes (%d): %v\n", len(sizes), sizes)
|
||||
fmt.Printf("std dev: %v; targetSize %v\n", sd, targetSize)
|
||||
}
|
||||
|
||||
skipCount := 0
|
||||
count := 0
|
||||
// quickly eliminate bad row groupings
|
||||
startingCache := make(map[int]bool)
|
||||
// try to find a layout with all rows within 1.2*targetSize
|
||||
// skip options with a row that is 1.2*longer or shorter
|
||||
// Note: we want a low threshold to explore good options within attemptLimit,
|
||||
// but the best option may require a few rows that are far from the target size.
|
||||
okThreshold := 1.2
|
||||
// if we don't find a layout try 25% larger threshold
|
||||
thresholdStep := 0.25
|
||||
okThreshold := STARTING_THRESHOLD
|
||||
rowOk := func(row []*d2graph.Object, starting bool) (ok bool) {
|
||||
if starting {
|
||||
// we can cache results from starting positions since they repeat and don't change
|
||||
|
|
@ -509,21 +664,24 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
// if multiple nodes are too big, it isn't ok. but a single node can't shrink so only check here
|
||||
if rowSize > okThreshold*targetSize {
|
||||
skipCount++
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
// there may even be too many to skip
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
// row is too small to be good overall
|
||||
if rowSize < targetSize/okThreshold {
|
||||
skipCount++
|
||||
if skipCount >= SKIP_LIMIT {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var bestLayout [][]*d2graph.Object
|
||||
bestDist := math.MaxFloat64
|
||||
count := 0
|
||||
attemptLimit := 100_000
|
||||
// get all options for where to place these cuts, preferring later cuts over earlier cuts
|
||||
// with 5 objects and 2 cuts we have these options:
|
||||
// . A B C │ D │ E <- these cuts would produce: ┌A─┐ ┌B─┐ ┌C─┐
|
||||
|
|
@ -539,33 +697,94 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
if dist < bestDist {
|
||||
bestLayout = layout
|
||||
bestDist = dist
|
||||
fastIsBest = false
|
||||
} else if fastIsBest && dist == bestDist {
|
||||
// prefer ordered search solution to fast layout solution
|
||||
bestLayout = layout
|
||||
fastIsBest = false
|
||||
}
|
||||
count++
|
||||
// with few objects we can try all options to get best result but this won't scale, so only try up to 100k options
|
||||
return count >= attemptLimit
|
||||
return count >= ATTEMPT_LIMIT || skipCount >= SKIP_LIMIT
|
||||
}
|
||||
|
||||
// try at least 3 different okThresholds
|
||||
for i := 0; i < 3 || bestLayout == nil; i++ {
|
||||
// try number of different okThresholds depending on std deviation of sizes
|
||||
thresholdAttempts := int(math.Ceil(sd))
|
||||
if thresholdAttempts < MIN_THRESHOLD_ATTEMPTS {
|
||||
thresholdAttempts = MIN_THRESHOLD_ATTEMPTS
|
||||
} else if thresholdAttempts > MAX_THRESHOLD_ATTEMPTS {
|
||||
thresholdAttempts = MAX_THRESHOLD_ATTEMPTS
|
||||
}
|
||||
for i := 0; i < thresholdAttempts || bestLayout == nil; i++ {
|
||||
count = 0.
|
||||
skipCount = 0.
|
||||
iterDivisions(gd.objects, nCuts, tryDivision, rowOk)
|
||||
okThreshold += thresholdStep
|
||||
okThreshold += THRESHOLD_STEP_SIZE
|
||||
if debug {
|
||||
fmt.Printf("increasing ok threshold to %v\n", okThreshold)
|
||||
fmt.Printf("count %d, skip count %d, bestDist %v increasing ok threshold to %v\n", count, skipCount, bestDist, okThreshold)
|
||||
}
|
||||
startingCache = make(map[int]bool)
|
||||
count = 0.
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("final count %d, skip count %d\n", count, skipCount)
|
||||
if skipCount == 0 {
|
||||
// threshold isn't skipping anything so increasing it won't help
|
||||
break
|
||||
}
|
||||
// okThreshold isn't high enough yet, we skipped every option so don't count it
|
||||
if count == 0 && thresholdAttempts < MAX_THRESHOLD_ATTEMPTS {
|
||||
thresholdAttempts++
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Printf("best layout: %v\n", layoutString(bestLayout, sizes))
|
||||
}
|
||||
return bestLayout
|
||||
}
|
||||
|
||||
func sum(values []float64) float64 {
|
||||
s := 0.
|
||||
for _, v := range values {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func avg(values []float64) float64 {
|
||||
return sum(values) / float64(len(values))
|
||||
}
|
||||
|
||||
func variance(values []float64) float64 {
|
||||
mean := avg(values)
|
||||
total := 0.
|
||||
for _, value := range values {
|
||||
dev := mean - value
|
||||
total += dev * dev
|
||||
}
|
||||
return total / float64(len(values))
|
||||
}
|
||||
|
||||
func stddev(values []float64) float64 {
|
||||
return math.Sqrt(variance(values))
|
||||
}
|
||||
|
||||
func (gd *gridDiagram) fastLayout(targetSize float64, nCuts int, columns bool) (layout [][]*d2graph.Object) {
|
||||
var gap float64
|
||||
if columns {
|
||||
gap = float64(gd.verticalGap)
|
||||
} else {
|
||||
gap = float64(gd.horizontalGap)
|
||||
}
|
||||
|
||||
// try fast layout algorithm, see if it is better than first 1mil attempts
|
||||
debt := 0.
|
||||
fastDivision := make([]int, 0, nCuts)
|
||||
rowSize := 0.
|
||||
for i := 0; i < len(gd.objects); i++ {
|
||||
o := gd.objects[i]
|
||||
size := getSize(o)
|
||||
var size float64
|
||||
if columns {
|
||||
size = o.Height
|
||||
} else {
|
||||
size = o.Width
|
||||
}
|
||||
if rowSize == 0 {
|
||||
if size > targetSize-debt {
|
||||
fastDivision = append(fastDivision, i-1)
|
||||
|
|
@ -588,14 +807,23 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
|
|||
}
|
||||
}
|
||||
if len(fastDivision) == nCuts {
|
||||
layout := genLayout(gd.objects, fastDivision)
|
||||
dist := getDistToTarget(layout, targetSize, float64(gd.horizontalGap), float64(gd.verticalGap), columns)
|
||||
if dist < bestDist {
|
||||
bestLayout = layout
|
||||
bestDist = dist
|
||||
}
|
||||
layout = genLayout(gd.objects, fastDivision)
|
||||
}
|
||||
return bestLayout
|
||||
|
||||
return layout
|
||||
}
|
||||
|
||||
func layoutString(layout [][]*d2graph.Object, sizes []float64) string {
|
||||
buf := &bytes.Buffer{}
|
||||
i := 0
|
||||
fmt.Fprintf(buf, "[\n")
|
||||
for _, r := range layout {
|
||||
vals := sizes[i : i+len(r)]
|
||||
fmt.Fprintf(buf, "%v:\t%v\n", sum(vals), vals)
|
||||
i += len(r)
|
||||
}
|
||||
fmt.Fprintf(buf, "]\n")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// process current division, return true to stop iterating
|
||||
|
|
@ -704,25 +932,42 @@ func cleanup(graph *d2graph.Graph, gridDiagrams map[string]*gridDiagram, objects
|
|||
})
|
||||
}()
|
||||
|
||||
var restore func(obj *d2graph.Object)
|
||||
restore = func(obj *d2graph.Object) {
|
||||
gd, exists := gridDiagrams[obj.AbsID()]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
|
||||
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
|
||||
if obj.GridGap != nil || obj.HorizontalGap != nil {
|
||||
horizontalPadding = gd.horizontalGap
|
||||
}
|
||||
if obj.GridGap != nil || obj.VerticalGap != nil {
|
||||
verticalPadding = gd.verticalGap
|
||||
}
|
||||
|
||||
// shift the grid from (0, 0)
|
||||
gd.shift(
|
||||
obj.TopLeft.X+float64(horizontalPadding),
|
||||
obj.TopLeft.Y+float64(verticalPadding),
|
||||
)
|
||||
gd.cleanup(obj, graph)
|
||||
|
||||
for _, child := range obj.ChildrenArray {
|
||||
restore(child)
|
||||
}
|
||||
}
|
||||
|
||||
if graph.Root.IsGridDiagram() {
|
||||
gd, exists := gridDiagrams[graph.Root.AbsID()]
|
||||
if exists {
|
||||
gd.cleanup(graph.Root, graph)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, obj := range graph.Objects {
|
||||
gd, exists := gridDiagrams[obj.AbsID()]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
// shift the grid from (0, 0)
|
||||
gd.shift(
|
||||
obj.TopLeft.X+CONTAINER_PADDING,
|
||||
obj.TopLeft.Y+CONTAINER_PADDING,
|
||||
)
|
||||
gd.cleanup(obj, graph)
|
||||
restore(obj)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,62 +148,14 @@ func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (constantNearGr
|
|||
}
|
||||
_, isConst := d2graph.NearConstants[d2graph.Key(obj.NearKey)[0]]
|
||||
if isConst {
|
||||
descendantObjects, edges := pluckObjAndEdges(g, obj)
|
||||
|
||||
tempGraph := d2graph.NewGraph()
|
||||
tempGraph.Root.ChildrenArray = []*d2graph.Object{obj}
|
||||
tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
|
||||
|
||||
for _, descendantObj := range descendantObjects {
|
||||
descendantObj.Graph = tempGraph
|
||||
}
|
||||
tempGraph.Objects = descendantObjects
|
||||
tempGraph.Edges = edges
|
||||
|
||||
tempGraph := g.ExtractAsNestedGraph(obj)
|
||||
constantNearGraphs = append(constantNearGraphs, tempGraph)
|
||||
|
||||
i--
|
||||
delete(obj.Parent.Children, strings.ToLower(obj.ID))
|
||||
for i := 0; i < len(obj.Parent.ChildrenArray); i++ {
|
||||
if obj.Parent.ChildrenArray[i] == obj {
|
||||
obj.Parent.ChildrenArray = append(obj.Parent.ChildrenArray[:i], obj.Parent.ChildrenArray[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
obj.Parent = tempGraph.Root
|
||||
}
|
||||
}
|
||||
return constantNearGraphs
|
||||
}
|
||||
|
||||
func pluckObjAndEdges(g *d2graph.Graph, obj *d2graph.Object) (descendantsObjects []*d2graph.Object, edges []*d2graph.Edge) {
|
||||
for i := 0; i < len(g.Edges); i++ {
|
||||
edge := g.Edges[i]
|
||||
if edge.Src == obj || edge.Dst == obj {
|
||||
edges = append(edges, edge)
|
||||
g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(g.Objects); i++ {
|
||||
temp := g.Objects[i]
|
||||
if temp.AbsID() == obj.AbsID() {
|
||||
descendantsObjects = append(descendantsObjects, obj)
|
||||
g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
|
||||
for _, child := range obj.ChildrenArray {
|
||||
subObjects, subEdges := pluckObjAndEdges(g, child)
|
||||
descendantsObjects = append(descendantsObjects, subObjects...)
|
||||
edges = append(edges, subEdges...)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return descendantsObjects, edges
|
||||
}
|
||||
|
||||
// boundingBox gets the center of the graph as defined by shapes
|
||||
// The bounds taking into consideration only shapes gives more of a feeling of true center
|
||||
// It differs from d2target.BoundingBox which needs to include every visible thing
|
||||
|
|
|
|||
714
d2oracle/edit.go
|
|
@ -68,6 +68,172 @@ func Set(g *d2graph.Graph, key string, tag, value *string) (_ *d2graph.Graph, er
|
|||
return recompile(g)
|
||||
}
|
||||
|
||||
func ReconnectEdge(g *d2graph.Graph, edgeKey string, srcKey, dstKey *string) (_ *d2graph.Graph, err error) {
|
||||
mk, err := d2parser.ParseMapKey(edgeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(mk.Edges) == 0 {
|
||||
return nil, errors.New("edgeKey must be an edge")
|
||||
}
|
||||
|
||||
if mk.EdgeIndex == nil {
|
||||
return nil, errors.New("edgeKey must refer to an existing edge")
|
||||
}
|
||||
|
||||
edgeTrimCommon(mk)
|
||||
obj := g.Root
|
||||
if mk.Key != nil {
|
||||
var ok bool
|
||||
obj, ok = g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
if !ok {
|
||||
return nil, errors.New("edge not found")
|
||||
}
|
||||
}
|
||||
|
||||
edge, ok := obj.HasEdge(mk)
|
||||
if !ok {
|
||||
return nil, errors.New("edge not found")
|
||||
}
|
||||
|
||||
if srcKey != nil {
|
||||
if edge.Src.AbsID() == *srcKey {
|
||||
srcKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
if dstKey != nil {
|
||||
if edge.Dst.AbsID() == *dstKey {
|
||||
dstKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
if srcKey == nil && dstKey == nil {
|
||||
return g, nil
|
||||
}
|
||||
|
||||
var src *d2graph.Object
|
||||
var dst *d2graph.Object
|
||||
if srcKey != nil {
|
||||
srcmk, err := d2parser.ParseMapKey(*srcKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src, ok = g.Root.HasChild(d2graph.Key(srcmk.Key))
|
||||
if !ok {
|
||||
return nil, errors.New("newSrc not found")
|
||||
}
|
||||
}
|
||||
if dstKey != nil {
|
||||
dstmk, err := d2parser.ParseMapKey(*dstKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst, ok = g.Root.HasChild(d2graph.Key(dstmk.Key))
|
||||
if !ok {
|
||||
return nil, errors.New("newDst not found")
|
||||
}
|
||||
}
|
||||
|
||||
ref := edge.References[0]
|
||||
|
||||
// for loops where only one end is changing, node is always ensured
|
||||
if edge.Src != edge.Dst && (srcKey == nil || dstKey == nil) {
|
||||
var refEdges []*d2ast.Edge
|
||||
for _, ref := range edge.References {
|
||||
refEdges = append(refEdges, ref.Edge)
|
||||
}
|
||||
|
||||
if srcKey != nil {
|
||||
ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Src, true)
|
||||
}
|
||||
if dstKey != nil {
|
||||
ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Dst, false)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range edge.References {
|
||||
ref := edge.References[i]
|
||||
// it's a chain
|
||||
if len(ref.MapKey.Edges) > 1 && ref.MapKey.EdgeIndex == nil {
|
||||
splitChain := true
|
||||
// Changing the start of a chain is okay
|
||||
if ref.MapKeyEdgeIndex == 0 && dstKey == nil {
|
||||
splitChain = false
|
||||
}
|
||||
// Changing the end of a chain is okay
|
||||
if ref.MapKeyEdgeIndex == len(ref.MapKey.Edges)-1 && srcKey == nil {
|
||||
splitChain = false
|
||||
}
|
||||
if splitChain {
|
||||
tmp := *ref.MapKey
|
||||
mk2 := &tmp
|
||||
mk2.Edges = []*d2ast.Edge{ref.MapKey.Edges[ref.MapKeyEdgeIndex]}
|
||||
ref.Scope.InsertAfter(ref.MapKey, mk2)
|
||||
|
||||
if ref.MapKeyEdgeIndex < len(ref.MapKey.Edges)-1 {
|
||||
tmp := *ref.MapKey
|
||||
mk2 := &tmp
|
||||
mk2.Edges = ref.MapKey.Edges[ref.MapKeyEdgeIndex+1:]
|
||||
ref.Scope.InsertAfter(ref.MapKey, mk2)
|
||||
}
|
||||
ref.MapKey.Edges = ref.MapKey.Edges[:ref.MapKeyEdgeIndex]
|
||||
}
|
||||
}
|
||||
|
||||
if src != nil {
|
||||
srcmk, _ := d2parser.ParseMapKey(*srcKey)
|
||||
ref.Edge.Src = srcmk.Key
|
||||
newPath, err := pathFromScopeObj(g, srcmk, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref.Edge.Src.Path = newPath
|
||||
}
|
||||
if dst != nil {
|
||||
dstmk, _ := d2parser.ParseMapKey(*dstKey)
|
||||
ref.Edge.Dst = dstmk.Key
|
||||
newPath, err := pathFromScopeObj(g, dstmk, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref.Edge.Dst.Path = newPath
|
||||
}
|
||||
}
|
||||
|
||||
return recompile(g)
|
||||
}
|
||||
|
||||
func pathFromScopeKey(g *d2graph.Graph, key *d2ast.Key, scopeak []string) ([]*d2ast.StringBox, error) {
|
||||
ak2 := d2graph.Key(key.Key)
|
||||
|
||||
commonPath := getCommonPath(scopeak, ak2)
|
||||
|
||||
var newPath []*d2ast.StringBox
|
||||
// Move out to most common scope
|
||||
for i := len(commonPath); i < len(scopeak); i++ {
|
||||
newPath = append(newPath, d2ast.MakeValueBox(d2ast.RawString("_", true)).StringBox())
|
||||
}
|
||||
// From most common scope, target the toKey
|
||||
newPath = append(newPath, key.Key.Path[len(commonPath):]...)
|
||||
|
||||
return newPath, nil
|
||||
}
|
||||
|
||||
func pathFromScopeObj(g *d2graph.Graph, key *d2ast.Key, fromScope *d2graph.Object) ([]*d2ast.StringBox, error) {
|
||||
// We don't want this to be underscore-resolved scope. We want to ignore underscores
|
||||
var scopeak []string
|
||||
if fromScope != g.Root {
|
||||
scopek, err := d2parser.ParseKey(fromScope.AbsID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scopeak = d2graph.Key(scopek)
|
||||
}
|
||||
return pathFromScopeKey(g, key, scopeak)
|
||||
}
|
||||
|
||||
func recompile(g *d2graph.Graph) (*d2graph.Graph, error) {
|
||||
s := d2format.Format(g.AST)
|
||||
g, err := d2compiler.Compile(g.AST.Range.Path, strings.NewReader(s), nil)
|
||||
|
|
@ -606,7 +772,7 @@ func Delete(g *d2graph.Graph, key string) (_ *d2graph.Graph, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := updateNear(prevG, g, &key, nil); err != nil {
|
||||
if err := updateNear(prevG, g, &key, nil, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -753,7 +919,27 @@ func renameConflictsToParent(g *d2graph.Graph, key *d2ast.KeyPath) (*d2graph.Gra
|
|||
hoistedAbsKey.Path = append(hoistedAbsKey.Path, ref.Key.Path[:ref.KeyPathIndex]...)
|
||||
hoistedAbsKey.Path = append(hoistedAbsKey.Path, absKey.Path[len(absKey.Path)-1])
|
||||
|
||||
uniqueKeyStr, _, err := generateUniqueKey(g, strings.Join(d2graph.Key(hoistedAbsKey), "."), ignored, newIDs)
|
||||
// Can't generate a key that'd conflict with sibling
|
||||
var siblingHoistedIDs []string
|
||||
for _, otherAbsKey := range absKeys {
|
||||
if absKey == otherAbsKey {
|
||||
continue
|
||||
}
|
||||
ida := d2graph.Key(otherAbsKey)
|
||||
absKeyStr := strings.Join(ida, ".")
|
||||
if _, ok := dedupedRenames[absKeyStr]; ok {
|
||||
continue
|
||||
}
|
||||
hoistedAbsKey, err := d2parser.ParseKey(ref.ScopeObj.AbsID())
|
||||
if err != nil {
|
||||
hoistedAbsKey = &d2ast.KeyPath{}
|
||||
}
|
||||
hoistedAbsKey.Path = append(hoistedAbsKey.Path, ref.Key.Path[:ref.KeyPathIndex]...)
|
||||
hoistedAbsKey.Path = append(hoistedAbsKey.Path, otherAbsKey.Path[len(otherAbsKey.Path)-1])
|
||||
siblingHoistedIDs = append(siblingHoistedIDs, strings.Join(d2graph.Key(hoistedAbsKey), "."))
|
||||
}
|
||||
|
||||
uniqueKeyStr, _, err := generateUniqueKey(g, strings.Join(d2graph.Key(hoistedAbsKey), "."), ignored, append(newIDs, siblingHoistedIDs...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -789,7 +975,7 @@ func renameConflictsToParent(g *d2graph.Graph, key *d2ast.KeyPath) (*d2graph.Gra
|
|||
}
|
||||
for _, k := range renameOrder {
|
||||
var err error
|
||||
g, err = move(g, k, renames[k])
|
||||
g, err = move(g, k, renames[k], false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1123,12 +1309,12 @@ func ensureNode(g *d2graph.Graph, excludedEdges []*d2ast.Edge, scopeObj *d2graph
|
|||
}
|
||||
}
|
||||
|
||||
func Rename(g *d2graph.Graph, key, newName string) (_ *d2graph.Graph, err error) {
|
||||
func Rename(g *d2graph.Graph, key, newName string) (_ *d2graph.Graph, newKey string, err error) {
|
||||
defer xdefer.Errorf(&err, "failed to rename %#v to %#v", key, newName)
|
||||
|
||||
mk, err := d2parser.ParseMapKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(mk.Edges) > 0 && mk.EdgeKey == nil {
|
||||
|
|
@ -1136,7 +1322,7 @@ func Rename(g *d2graph.Graph, key, newName string) (_ *d2graph.Graph, err error)
|
|||
// Maybe we remove Rename and just have Move.
|
||||
mk2, err := d2parser.ParseMapKey(newName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
mk2.Key = mk.Key
|
||||
|
|
@ -1144,13 +1330,25 @@ func Rename(g *d2graph.Graph, key, newName string) (_ *d2graph.Graph, err error)
|
|||
} else {
|
||||
_, ok := d2graph.ReservedKeywords[newName]
|
||||
if ok {
|
||||
return nil, fmt.Errorf("cannot rename to reserved keyword: %#v", newName)
|
||||
return nil, "", fmt.Errorf("cannot rename to reserved keyword: %#v", newName)
|
||||
}
|
||||
if mk.Key != nil {
|
||||
obj, ok := g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("key does not exist")
|
||||
}
|
||||
// If attempt to name something "x", but "x" already exists, rename it "x 2" instead
|
||||
generatedName, _, err := generateUniqueKey(g, newName, obj, nil)
|
||||
if err == nil {
|
||||
newName = generatedName
|
||||
}
|
||||
}
|
||||
// TODO: Handle mk.EdgeKey
|
||||
mk.Key.Path[len(mk.Key.Path)-1] = d2ast.MakeValueBox(d2ast.RawString(newName, true)).StringBox()
|
||||
}
|
||||
|
||||
return move(g, key, d2format.Format(mk))
|
||||
g, err = move(g, key, d2format.Format(mk), false)
|
||||
return g, newName, err
|
||||
}
|
||||
|
||||
func trimReservedSuffix(path []*d2ast.StringBox) []*d2ast.StringBox {
|
||||
|
|
@ -1163,12 +1361,12 @@ func trimReservedSuffix(path []*d2ast.StringBox) []*d2ast.StringBox {
|
|||
}
|
||||
|
||||
// Does not handle edge keys, on account of edge keys can only be reserved, e.g. (a->b).style.color: red
|
||||
func Move(g *d2graph.Graph, key, newKey string) (_ *d2graph.Graph, err error) {
|
||||
func Move(g *d2graph.Graph, key, newKey string, includeDescendants bool) (_ *d2graph.Graph, err error) {
|
||||
defer xdefer.Errorf(&err, "failed to move: %#v to %#v", key, newKey)
|
||||
return move(g, key, newKey)
|
||||
return move(g, key, newKey, includeDescendants)
|
||||
}
|
||||
|
||||
func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
||||
func move(g *d2graph.Graph, key, newKey string, includeDescendants bool) (*d2graph.Graph, error) {
|
||||
if key == newKey {
|
||||
return g, nil
|
||||
}
|
||||
|
|
@ -1225,7 +1423,7 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
|
||||
isCrossScope := strings.Join(ak[:len(ak)-1], ".") != strings.Join(ak2[:len(ak2)-1], ".")
|
||||
|
||||
if isCrossScope {
|
||||
if isCrossScope && !includeDescendants {
|
||||
g, err = renameConflictsToParent(g, mk.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1361,7 +1559,14 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
firstNonUnderscoreIndex := 0
|
||||
ida := d2graph.Key(ref.Key)
|
||||
for i, id := range ida {
|
||||
if id != "_" {
|
||||
firstNonUnderscoreIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
resolvedObj, resolvedIDA, err := d2graph.ResolveUnderscoreKey(ida, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1369,6 +1574,8 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
if resolvedObj != obj {
|
||||
ida = resolvedIDA
|
||||
}
|
||||
// e.g. "a.b.shape: circle"
|
||||
_, endsWithReserved := d2graph.ReservedKeywords[ida[len(ida)-1]]
|
||||
ida = go2.Filter(ida, func(x string) bool {
|
||||
_, ok := d2graph.ReservedKeywords[x]
|
||||
return !ok
|
||||
|
|
@ -1384,14 +1591,16 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
// 4. Slice.
|
||||
// -- The key is moving from its current scope out to a less nested scope
|
||||
if isCrossScope {
|
||||
if len(ida) == 1 {
|
||||
if (!includeDescendants && len(ida) == 1) || (includeDescendants && ref.KeyPathIndex == firstNonUnderscoreIndex) {
|
||||
// 1. Transplant
|
||||
absKey, err := d2parser.ParseKey(ref.ScopeObj.AbsID())
|
||||
if err != nil {
|
||||
absKey = &d2ast.KeyPath{}
|
||||
}
|
||||
absKey.Path = append(absKey.Path, ref.Key.Path...)
|
||||
hoistRefChildren(g, absKey, ref)
|
||||
if !includeDescendants {
|
||||
hoistRefChildren(g, absKey, ref)
|
||||
}
|
||||
deleteFromMap(ref.Scope, ref.MapKey)
|
||||
detachedMK := &d2ast.Key{Primary: ref.MapKey.Primary, Key: cloneKey(ref.MapKey.Key)}
|
||||
detachedMK.Key.Path = go2.Filter(detachedMK.Key.Path, func(x *d2ast.StringBox) bool {
|
||||
|
|
@ -1399,39 +1608,103 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
})
|
||||
detachedMK.Value = ref.MapKey.Value
|
||||
if ref.MapKey != nil && ref.MapKey.Value.Map != nil {
|
||||
detachedMK.Value.Map = &d2ast.Map{
|
||||
Range: ref.MapKey.Value.Map.Range,
|
||||
}
|
||||
for _, n := range ref.MapKey.Value.Map.Nodes {
|
||||
if n.MapKey == nil {
|
||||
continue
|
||||
// Without including descendants, just copy over the reserved
|
||||
if !includeDescendants {
|
||||
detachedMK.Value.Map = &d2ast.Map{
|
||||
Range: ref.MapKey.Value.Map.Range,
|
||||
}
|
||||
if n.MapKey.Key != nil {
|
||||
_, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()]
|
||||
if ok {
|
||||
detachedMK.Value.Map.Nodes = append(detachedMK.Value.Map.Nodes, n)
|
||||
for _, n := range ref.MapKey.Value.Map.Nodes {
|
||||
if n.MapKey == nil {
|
||||
continue
|
||||
}
|
||||
if n.MapKey.Key != nil {
|
||||
_, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()]
|
||||
if ok {
|
||||
detachedMK.Value.Map.Nodes = append(detachedMK.Value.Map.Nodes, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(detachedMK.Value.Map.Nodes) == 0 {
|
||||
detachedMK.Value.Map = nil
|
||||
}
|
||||
} else {
|
||||
// Usually copy everything as is when including descendants
|
||||
// The exception is underscored keys, which need to be updated
|
||||
for _, n := range ref.MapKey.Value.Map.Nodes {
|
||||
if n.MapKey == nil {
|
||||
continue
|
||||
}
|
||||
if n.MapKey.Key != nil {
|
||||
if n.MapKey.Key.Path[0].Unbox().ScalarString() == "_" {
|
||||
resolvedParent, resolvedScopeKey, err := d2graph.ResolveUnderscoreKey(d2graph.Key(n.MapKey.Key), obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newPath, err := pathFromScopeKey(g, &d2ast.Key{Key: d2ast.MakeKeyPath(append(resolvedParent.AbsIDArray(), resolvedScopeKey...))}, ak2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.MapKey.Key.Path = newPath
|
||||
}
|
||||
}
|
||||
for _, e := range n.MapKey.Edges {
|
||||
if e.Src.Path[0].Unbox().ScalarString() == "_" {
|
||||
resolvedParent, resolvedScopeKey, err := d2graph.ResolveUnderscoreKey(d2graph.Key(e.Src), obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newPath, err := pathFromScopeKey(g, &d2ast.Key{Key: d2ast.MakeKeyPath(append(resolvedParent.AbsIDArray(), resolvedScopeKey...))}, ak2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Src.Path = newPath
|
||||
}
|
||||
if e.Dst.Path[0].Unbox().ScalarString() == "_" {
|
||||
resolvedParent, resolvedScopeKey, err := d2graph.ResolveUnderscoreKey(d2graph.Key(e.Dst), obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newPath, err := pathFromScopeKey(g, &d2ast.Key{Key: d2ast.MakeKeyPath(append(resolvedParent.AbsIDArray(), resolvedScopeKey...))}, ak2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Dst.Path = newPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(detachedMK.Value.Map.Nodes) == 0 {
|
||||
detachedMK.Value.Map = nil
|
||||
}
|
||||
}
|
||||
|
||||
appendUniqueMapKey(toScope, detachedMK)
|
||||
} else if len(ida) > 1 && (!isExplicit || go2.Contains(mostNestedRefs, ref)) {
|
||||
} else if len(ida) > 1 && (endsWithReserved || !isExplicit || go2.Contains(mostNestedRefs, ref)) {
|
||||
// 2. Split
|
||||
detachedMK := &d2ast.Key{Key: cloneKey(ref.MapKey.Key)}
|
||||
detachedMK.Key.Path = []*d2ast.StringBox{ref.Key.Path[ref.KeyPathIndex]}
|
||||
if ref.KeyPathIndex == len(ref.Key.Path)-1 {
|
||||
if includeDescendants {
|
||||
detachedMK.Key.Path = append([]*d2ast.StringBox{}, ref.Key.Path[ref.KeyPathIndex:]...)
|
||||
} else {
|
||||
detachedMK.Key.Path = []*d2ast.StringBox{ref.Key.Path[ref.KeyPathIndex]}
|
||||
}
|
||||
if includeDescendants {
|
||||
detachedMK.Value = ref.MapKey.Value
|
||||
ref.MapKey.Value = d2ast.ValueBox{}
|
||||
} else if ref.KeyPathIndex == len(filterReservedPath(ref.Key.Path))-1 {
|
||||
withReserved, withoutReserved := filterReserved(ref.MapKey.Value)
|
||||
detachedMK.Value = withReserved
|
||||
ref.MapKey.Value = withoutReserved
|
||||
detachedMK.Key.Path = append([]*d2ast.StringBox{}, ref.Key.Path[ref.KeyPathIndex:]...)
|
||||
ref.Key.Path = ref.Key.Path[:ref.KeyPathIndex+1]
|
||||
}
|
||||
if includeDescendants {
|
||||
ref.Key.Path = ref.Key.Path[:ref.KeyPathIndex]
|
||||
} else {
|
||||
ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex], ref.Key.Path[ref.KeyPathIndex+1:]...)
|
||||
}
|
||||
ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex], ref.Key.Path[ref.KeyPathIndex+1:]...)
|
||||
appendUniqueMapKey(toScope, detachedMK)
|
||||
} else if len(getCommonPath(ak, ak2)) > 0 {
|
||||
// 3. Extend
|
||||
// This case does not make sense for includeDescendants
|
||||
newKeyPath := ref.Key.Path[:ref.KeyPathIndex]
|
||||
newKeyPath = append(newKeyPath, mk2.Key.Path[len(getCommonPath(ak, ak2)):]...)
|
||||
ref.Key.Path = append(newKeyPath, ref.Key.Path[ref.KeyPathIndex+1:]...)
|
||||
|
|
@ -1476,48 +1749,76 @@ func move(g *d2graph.Graph, key, newKey string) (*d2graph.Graph, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
// We don't want this to be underscore-resolved scope. We want to ignore underscores
|
||||
var scopeak []string
|
||||
if ref.ScopeObj != g.Root {
|
||||
scopek, err := d2parser.ParseKey(ref.ScopeObj.AbsID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
firstNonUnderscoreIndex := 0
|
||||
ida := d2graph.Key(ref.Key)
|
||||
for i, id := range ida {
|
||||
if id != "_" {
|
||||
firstNonUnderscoreIndex = i
|
||||
break
|
||||
}
|
||||
scopeak = d2graph.Key(scopek)
|
||||
}
|
||||
commonPath := getCommonPath(scopeak, ak2)
|
||||
|
||||
// When moving a node out of an edge, e.g. the `b` out of `a.b.c -> ...`,
|
||||
// The edge needs to continue targeting the same thing (c)
|
||||
if ref.KeyPathIndex != len(ref.Key.Path)-1 {
|
||||
// When moving a node out of an edge, e.g. the `b` out of `a.b.c -> ...`,
|
||||
// The edge needs to continue targeting the same thing (c)
|
||||
// Split
|
||||
detachedMK := &d2ast.Key{
|
||||
Key: cloneKey(ref.Key),
|
||||
}
|
||||
detachedMK.Key.Path = []*d2ast.StringBox{ref.Key.Path[ref.KeyPathIndex]}
|
||||
appendUniqueMapKey(toScope, detachedMK)
|
||||
oldPath, err := pathFromScopeObj(g, mk, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newPath, err := pathFromScopeObj(g, mk2, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if includeDescendants {
|
||||
// When including descendants, the only thing that gets dropped, if any, is the uncommon leading path of new key and key
|
||||
// E.g. when moving `a.b` to `x.b` with edge ref key of `a.b.c`, then changing it to `x.b.c` will drop `a`
|
||||
diff := len(oldPath) - len(newPath)
|
||||
// Only need to check uncommon path if the lengths are the same
|
||||
if diff == 0 {
|
||||
diff = len(getUncommonPath(d2graph.Key(&d2ast.KeyPath{Path: oldPath}), d2graph.Key(&d2ast.KeyPath{Path: newPath})))
|
||||
}
|
||||
// If the old key is longer than the new key, we already know all the diff would be dropped
|
||||
if diff > 0 && ref.KeyPathIndex != firstNonUnderscoreIndex {
|
||||
detachedMK.Key.Path = append([]*d2ast.StringBox{}, ref.Key.Path[ref.KeyPathIndex-diff:ref.KeyPathIndex]...)
|
||||
appendUniqueMapKey(ref.Scope, detachedMK)
|
||||
}
|
||||
} else {
|
||||
detachedMK.Key.Path = []*d2ast.StringBox{ref.Key.Path[ref.KeyPathIndex]}
|
||||
appendUniqueMapKey(toScope, detachedMK)
|
||||
}
|
||||
|
||||
ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex], ref.Key.Path[ref.KeyPathIndex+1:]...)
|
||||
if includeDescendants {
|
||||
ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex-len(oldPath)+1], append(newPath, ref.Key.Path[ref.KeyPathIndex+1:]...)...)
|
||||
} else {
|
||||
ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex], ref.Key.Path[ref.KeyPathIndex+1:]...)
|
||||
}
|
||||
} else {
|
||||
// When moving a node connected to an edge, we have to ensure parents continue to exist
|
||||
// e.g. the `c` out of `a.b.c -> ...`
|
||||
// `a.b` needs to exist
|
||||
newPath, err := pathFromScopeObj(g, mk2, ref.ScopeObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(go2.Filter(ref.Key.Path, func(x *d2ast.StringBox) bool { return x.Unbox().ScalarString() != "_" })) > 1 {
|
||||
detachedK := cloneKey(ref.Key)
|
||||
detachedK.Path = detachedK.Path[:len(detachedK.Path)-1]
|
||||
ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, detachedK, false)
|
||||
}
|
||||
// Move out to most common scope
|
||||
ref.Key.Path = nil
|
||||
for i := len(commonPath); i < len(scopeak); i++ {
|
||||
ref.Key.Path = append(ref.Key.Path, d2ast.MakeValueBox(d2ast.RawString("_", true)).StringBox())
|
||||
|
||||
if includeDescendants {
|
||||
ref.Key.Path = append(newPath, ref.Key.Path[go2.Min(len(ref.Key.Path), ref.KeyPathIndex+len(newPath)):]...)
|
||||
} else {
|
||||
ref.Key.Path = newPath
|
||||
}
|
||||
// From most common scope, target the toKey
|
||||
ref.Key.Path = append(ref.Key.Path, mk2.Key.Path[len(commonPath):]...)
|
||||
}
|
||||
}
|
||||
|
||||
if err := updateNear(prevG, g, &key, &newKey); err != nil {
|
||||
if err := updateNear(prevG, g, &key, &newKey, includeDescendants); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -1592,7 +1893,7 @@ func filterReserved(value d2ast.ValueBox) (with, without d2ast.ValueBox) {
|
|||
|
||||
// updateNear updates all the Near fields
|
||||
// prevG is the graph before the update (i.e. deletion, rename, move)
|
||||
func updateNear(prevG, g *d2graph.Graph, from, to *string) error {
|
||||
func updateNear(prevG, g *d2graph.Graph, from, to *string, includeDescendants bool) error {
|
||||
mk, _ := d2parser.ParseMapKey(*from)
|
||||
if len(mk.Edges) > 0 {
|
||||
return nil
|
||||
|
|
@ -1603,6 +1904,13 @@ func updateNear(prevG, g *d2graph.Graph, from, to *string) error {
|
|||
if len(mk.Key.Path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO get rid of repetition
|
||||
|
||||
// Update all the `near` keys that are one level nested
|
||||
// x: {
|
||||
// near: z
|
||||
// }
|
||||
for _, obj := range g.Objects {
|
||||
if obj.Map == nil {
|
||||
continue
|
||||
|
|
@ -1637,7 +1945,7 @@ func updateNear(prevG, g *d2graph.Graph, from, to *string) error {
|
|||
n.MapKey.Value = d2ast.MakeValueBox(d2ast.RawString(v, false))
|
||||
}
|
||||
} else {
|
||||
deltas, err := MoveIDDeltas(tmpG, *from, *to)
|
||||
deltas, err := MoveIDDeltas(tmpG, *from, *to, includeDescendants)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1649,6 +1957,52 @@ func updateNear(prevG, g *d2graph.Graph, from, to *string) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update all the `near` keys that are flat (x.near: z)
|
||||
for _, obj := range g.Objects {
|
||||
for _, ref := range obj.References {
|
||||
if ref.MapKey == nil {
|
||||
continue
|
||||
}
|
||||
if ref.MapKey.Key == nil {
|
||||
continue
|
||||
}
|
||||
if len(ref.MapKey.Key.Path) == 0 {
|
||||
continue
|
||||
}
|
||||
if ref.MapKey.Key.Path[len(ref.MapKey.Key.Path)-1].Unbox().ScalarString() == "near" {
|
||||
k := ref.MapKey.Value.ScalarBox().Unbox().ScalarString()
|
||||
if strings.EqualFold(k, *from) && to == nil {
|
||||
deleteFromMap(obj.Map, ref.MapKey)
|
||||
} else {
|
||||
valueMK, err := d2parser.ParseMapKey(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpG, _ := recompile(prevG)
|
||||
appendMapKey(tmpG.AST, valueMK)
|
||||
if to == nil {
|
||||
deltas, err := DeleteIDDeltas(tmpG, *from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := deltas[k]; ok {
|
||||
ref.MapKey.Value = d2ast.MakeValueBox(d2ast.RawString(v, false))
|
||||
}
|
||||
} else {
|
||||
deltas, err := MoveIDDeltas(tmpG, *from, *to, includeDescendants)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := deltas[k]; ok {
|
||||
ref.MapKey.Value = d2ast.MakeValueBox(d2ast.RawString(v, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1691,55 +2045,133 @@ func ReparentIDDelta(g *d2graph.Graph, key, parentKey string) (string, error) {
|
|||
return id, nil
|
||||
}
|
||||
|
||||
func ReconnectEdgeIDDelta(g *d2graph.Graph, edgeID, srcID, dstID string) (string, error) {
|
||||
mk, err := d2parser.ParseMapKey(edgeID)
|
||||
func ReconnectEdgeIDDeltas(g *d2graph.Graph, edgeKey string, srcKey, dstKey *string) (deltas map[string]string, err error) {
|
||||
defer xdefer.Errorf(&err, "failed to get deltas for reconnect edge %#v", edgeKey)
|
||||
deltas = make(map[string]string)
|
||||
// Reconnection: nothing is created or destroyed, the edge just gets a new ID
|
||||
// For deltas, it's indices that change:
|
||||
// - old sibling edges may decrement index
|
||||
// -- happens when the edge is not the last edge index
|
||||
// - new sibling edges may increment index
|
||||
// -- happens when the edge is not the last edge index
|
||||
// - new edge of course always needs an entry
|
||||
|
||||
// The change happens at the first ref, since that is what changes index
|
||||
mk, err := d2parser.ParseMapKey(edgeKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(mk.Edges) == 0 {
|
||||
return nil, errors.New("edgeKey must be an edge")
|
||||
}
|
||||
|
||||
if mk.EdgeIndex == nil {
|
||||
return nil, errors.New("edgeKey must refer to an existing edge")
|
||||
}
|
||||
|
||||
edgeTrimCommon(mk)
|
||||
obj := g.Root
|
||||
if mk.Key != nil {
|
||||
var ok bool
|
||||
obj, ok = g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
if !ok {
|
||||
return "", errors.New("edge key not found")
|
||||
return nil, errors.New("edge not found")
|
||||
}
|
||||
}
|
||||
e, ok := obj.HasEdge(mk)
|
||||
edge, ok := obj.HasEdge(mk)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("edge %v not found", edgeID)
|
||||
return nil, errors.New("edge not found")
|
||||
}
|
||||
if e.Src.AbsID() == srcID && e.Dst.AbsID() == dstID {
|
||||
return edgeID, nil
|
||||
|
||||
if srcKey != nil {
|
||||
if edge.Src.AbsID() == *srcKey {
|
||||
srcKey = nil
|
||||
}
|
||||
}
|
||||
oldSrc := e.Src
|
||||
oldDst := e.Dst
|
||||
if e.Src.AbsID() != srcID {
|
||||
mk, err := d2parser.ParseMapKey(srcID)
|
||||
|
||||
if dstKey != nil {
|
||||
if edge.Dst.AbsID() == *dstKey {
|
||||
dstKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
if srcKey == nil && dstKey == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
newSrc := edge.Src
|
||||
newDst := edge.Dst
|
||||
var src *d2graph.Object
|
||||
var dst *d2graph.Object
|
||||
if srcKey != nil {
|
||||
srcmk, err := d2parser.ParseMapKey(*srcKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
src, ok := g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
src, ok = g.Root.HasChild(d2graph.Key(srcmk.Key))
|
||||
if !ok {
|
||||
return "", fmt.Errorf("src %v not found", srcID)
|
||||
return nil, errors.New("newSrc not found")
|
||||
}
|
||||
e.Src = src
|
||||
newSrc = src
|
||||
}
|
||||
if e.Dst.AbsID() != dstID {
|
||||
mk, err := d2parser.ParseMapKey(dstID)
|
||||
if dstKey != nil {
|
||||
dstmk, err := d2parser.ParseMapKey(*dstKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
dst, ok := g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
dst, ok = g.Root.HasChild(d2graph.Key(dstmk.Key))
|
||||
if !ok {
|
||||
return "", fmt.Errorf("dst %v not found", dstID)
|
||||
return nil, errors.New("newDst not found")
|
||||
}
|
||||
e.Dst = dst
|
||||
newDst = dst
|
||||
}
|
||||
newID := fmt.Sprintf("%s %s %s", e.Src.AbsID(), e.ArrowString(), e.Dst.AbsID())
|
||||
e.Src = oldSrc
|
||||
e.Dst = oldDst
|
||||
return newID, nil
|
||||
|
||||
// The first ref is always the definition
|
||||
firstRef := edge.References[0]
|
||||
line := firstRef.MapKey.Range.Start.Line
|
||||
newIndex := 0
|
||||
|
||||
// For the edge's own delta, it just needs to know how many edges came before it with the same src and dst
|
||||
for _, otherEdge := range g.Edges {
|
||||
if otherEdge.Src == newSrc && otherEdge.Dst == newDst {
|
||||
firstRef := otherEdge.References[0]
|
||||
if firstRef.MapKey.Range.Start.Line <= line {
|
||||
newIndex++
|
||||
}
|
||||
}
|
||||
if otherEdge.Src == edge.Src && otherEdge.Dst == edge.Dst && otherEdge.Index > edge.Index {
|
||||
before := otherEdge.AbsID()
|
||||
otherEdge.Index--
|
||||
after := otherEdge.AbsID()
|
||||
deltas[before] = after
|
||||
otherEdge.Index++
|
||||
}
|
||||
}
|
||||
|
||||
for _, otherEdge := range g.Edges {
|
||||
if otherEdge.Src == newSrc && otherEdge.Dst == newDst {
|
||||
if otherEdge.Index >= newIndex {
|
||||
before := otherEdge.AbsID()
|
||||
otherEdge.Index++
|
||||
after := otherEdge.AbsID()
|
||||
deltas[before] = after
|
||||
otherEdge.Index--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newEdge := &d2graph.Edge{
|
||||
Src: newSrc,
|
||||
Dst: newDst,
|
||||
SrcArrow: edge.SrcArrow,
|
||||
DstArrow: edge.DstArrow,
|
||||
Index: newIndex,
|
||||
}
|
||||
|
||||
deltas[edge.AbsID()] = newEdge.AbsID()
|
||||
|
||||
return deltas, nil
|
||||
}
|
||||
|
||||
// generateUniqueKey generates a unique key by appending a number after `prefix` such that it doesn't conflict with any IDs in `g`
|
||||
|
|
@ -1848,6 +2280,16 @@ func getCommonPath(a, b []string) []string {
|
|||
return out
|
||||
}
|
||||
|
||||
func getUncommonPath(a, b []string) []string {
|
||||
var out []string
|
||||
for i := 0; i < len(a) && i < len(b); i++ {
|
||||
if a[i] != b[i] {
|
||||
out = a[:i+1]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func edgeTrimCommon(mk *d2ast.Key) {
|
||||
if len(mk.Edges) != 1 {
|
||||
return
|
||||
|
|
@ -1866,7 +2308,7 @@ func edgeTrimCommon(mk *d2ast.Key) {
|
|||
}
|
||||
}
|
||||
|
||||
func MoveIDDeltas(g *d2graph.Graph, key, newKey string) (deltas map[string]string, err error) {
|
||||
func MoveIDDeltas(g *d2graph.Graph, key, newKey string, includeDescendants bool) (deltas map[string]string, err error) {
|
||||
defer xdefer.Errorf(&err, "failed to get deltas for move from %#v to %#v", key, newKey)
|
||||
deltas = make(map[string]string)
|
||||
|
||||
|
|
@ -1915,48 +2357,50 @@ func MoveIDDeltas(g *d2graph.Graph, key, newKey string) (deltas map[string]strin
|
|||
}
|
||||
}
|
||||
|
||||
for _, ch := range obj.ChildrenArray {
|
||||
chMK, err := d2parser.ParseMapKey(ch.AbsID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ida := d2graph.Key(chMK.Key)
|
||||
if ida[len(ida)-1] == ida[len(ida)-2] {
|
||||
continue
|
||||
}
|
||||
|
||||
hoistedAbsID := ch.ID
|
||||
if obj.Parent != g.Root {
|
||||
hoistedAbsID = obj.Parent.AbsID() + "." + ch.ID
|
||||
}
|
||||
hoistedMK, err := d2parser.ParseMapKey(hoistedAbsID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conflictsWithNewID := false
|
||||
for _, id := range newIDs {
|
||||
if id == d2format.Format(hoistedMK.Key) {
|
||||
conflictsWithNewID = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := g.Root.HasChild(d2graph.Key(hoistedMK.Key)); ok || conflictsWithNewID {
|
||||
newKey, _, err := generateUniqueKey(g, hoistedAbsID, ignored, newIDs)
|
||||
if !includeDescendants {
|
||||
for _, ch := range obj.ChildrenArray {
|
||||
chMK, err := d2parser.ParseMapKey(ch.AbsID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newMK, err := d2parser.ParseMapKey(newKey)
|
||||
ida := d2graph.Key(chMK.Key)
|
||||
if ida[len(ida)-1] == ida[len(ida)-2] {
|
||||
continue
|
||||
}
|
||||
|
||||
hoistedAbsID := ch.ID
|
||||
if obj.Parent != g.Root {
|
||||
hoistedAbsID = obj.Parent.AbsID() + "." + ch.ID
|
||||
}
|
||||
hoistedMK, err := d2parser.ParseMapKey(hoistedAbsID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newAK := d2graph.Key(newMK.Key)
|
||||
conflictOldIDs[ch] = ch.ID
|
||||
conflictNewIDs[ch] = newAK[len(newAK)-1]
|
||||
newIDs = append(newIDs, d2format.Format(newMK.Key))
|
||||
} else {
|
||||
newIDs = append(newIDs, d2format.Format(hoistedMK.Key))
|
||||
|
||||
conflictsWithNewID := false
|
||||
for _, id := range newIDs {
|
||||
if id == d2format.Format(hoistedMK.Key) {
|
||||
conflictsWithNewID = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := g.Root.HasChild(d2graph.Key(hoistedMK.Key)); ok || conflictsWithNewID {
|
||||
newKey, _, err := generateUniqueKey(g, hoistedAbsID, ignored, newIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newMK, err := d2parser.ParseMapKey(newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newAK := d2graph.Key(newMK.Key)
|
||||
conflictOldIDs[ch] = ch.ID
|
||||
conflictNewIDs[ch] = newAK[len(newAK)-1]
|
||||
newIDs = append(newIDs, d2format.Format(newMK.Key))
|
||||
} else {
|
||||
newIDs = append(newIDs, d2format.Format(hoistedMK.Key))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1998,7 +2442,7 @@ func MoveIDDeltas(g *d2graph.Graph, key, newKey string) (deltas map[string]strin
|
|||
id := ak2[len(ak2)-1]
|
||||
|
||||
tmpRenames := func() func() {
|
||||
if isCrossScope {
|
||||
if isCrossScope && !includeDescendants {
|
||||
for _, ch := range obj.ChildrenArray {
|
||||
ch.Parent = obj.Parent
|
||||
}
|
||||
|
|
@ -2018,7 +2462,7 @@ func MoveIDDeltas(g *d2graph.Graph, key, newKey string) (deltas map[string]strin
|
|||
obj.ID = beforeObjID
|
||||
obj.Parent = prevParent
|
||||
|
||||
if isCrossScope {
|
||||
if isCrossScope && !includeDescendants {
|
||||
for _, ch := range obj.ChildrenArray {
|
||||
ch.Parent = obj
|
||||
}
|
||||
|
|
@ -2291,7 +2735,7 @@ func RenameIDDeltas(g *d2graph.Graph, key, newName string) (deltas map[string]st
|
|||
}
|
||||
|
||||
mk.Key.Path[len(mk.Key.Path)-1].Unbox().SetString(newName)
|
||||
uniqueKeyStr, _, err := generateUniqueKey(g, strings.Join(d2graph.Key(mk.Key), "."), nil, nil)
|
||||
uniqueKeyStr, _, err := generateUniqueKey(g, strings.Join(d2graph.Key(mk.Key), "."), obj, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -2305,19 +2749,23 @@ func RenameIDDeltas(g *d2graph.Graph, key, newName string) (deltas map[string]st
|
|||
beforeObjID := obj.ID
|
||||
|
||||
appendNodeDelta := func(ch *d2graph.Object) {
|
||||
beforeID := ch.AbsID()
|
||||
obj.ID = newNameKey
|
||||
deltas[beforeID] = ch.AbsID()
|
||||
obj.ID = beforeObjID
|
||||
if obj.ID != newNameKey {
|
||||
beforeID := ch.AbsID()
|
||||
obj.ID = newNameKey
|
||||
deltas[beforeID] = ch.AbsID()
|
||||
obj.ID = beforeObjID
|
||||
}
|
||||
}
|
||||
|
||||
appendEdgeDelta := func(ch *d2graph.Object) {
|
||||
for _, e := range obj.Graph.Edges {
|
||||
if e.Src == ch || e.Dst == ch {
|
||||
beforeID := e.AbsID()
|
||||
obj.ID = newNameKey
|
||||
deltas[beforeID] = e.AbsID()
|
||||
obj.ID = beforeObjID
|
||||
if obj.ID != newNameKey {
|
||||
beforeID := e.AbsID()
|
||||
obj.ID = newNameKey
|
||||
deltas[beforeID] = e.AbsID()
|
||||
obj.ID = beforeObjID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2391,3 +2839,13 @@ func getMostNestedRefs(obj *d2graph.Object) []d2graph.Reference {
|
|||
|
||||
return out
|
||||
}
|
||||
|
||||
func filterReservedPath(path []*d2ast.StringBox) (filtered []*d2ast.StringBox) {
|
||||
for _, box := range path {
|
||||
if _, ok := d2graph.ReservedKeywords[strings.ToLower(box.Unbox().ScalarString())]; ok {
|
||||
return
|
||||
}
|
||||
filtered = append(filtered, box)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,29 @@ package d2oracle
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
)
|
||||
|
||||
func GetChildrenIDs(g *d2graph.Graph, absID string) (ids []string, _ error) {
|
||||
mk, err := d2parser.ParseMapKey(absID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, ok := g.Root.HasChild(d2graph.Key(mk.Key))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v not found", absID)
|
||||
}
|
||||
|
||||
for _, ch := range obj.ChildrenArray {
|
||||
ids = append(ids, ch.AbsID())
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func GetParentID(g *d2graph.Graph, absID string) (string, error) {
|
||||
mk, err := d2parser.ParseMapKey(absID)
|
||||
if err != nil {
|
||||
|
|
@ -35,6 +54,24 @@ func GetEdge(g *d2graph.Graph, absID string) *d2graph.Edge {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GetObjOrder(g *d2graph.Graph) []string {
|
||||
var order []string
|
||||
|
||||
queue := []*d2graph.Object{g.Root}
|
||||
for len(queue) > 0 {
|
||||
curr := queue[0]
|
||||
queue = queue[1:]
|
||||
if curr != g.Root {
|
||||
order = append(order, curr.AbsID())
|
||||
}
|
||||
for _, ch := range curr.ChildrenArray {
|
||||
queue = append(queue, ch)
|
||||
}
|
||||
}
|
||||
|
||||
return order
|
||||
}
|
||||
|
||||
func IsLabelKeyID(key, label string) bool {
|
||||
mk, err := d2parser.ParseMapKey(key)
|
||||
if err != nil {
|
||||
|
|
@ -49,3 +86,18 @@ func IsLabelKeyID(key, label string) bool {
|
|||
|
||||
return mk.Key.Path[len(mk.Key.Path)-1].Unbox().ScalarString() == label
|
||||
}
|
||||
|
||||
func GetID(key string) string {
|
||||
mk, err := d2parser.ParseMapKey(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(mk.Edges) > 0 {
|
||||
return ""
|
||||
}
|
||||
if mk.Key == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return d2format.Format(d2ast.RawString(mk.Key.Path[len(mk.Key.Path)-1].Unbox().ScalarString(), true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,3 +18,9 @@ func TestIsLabelKeyID(t *testing.T) {
|
|||
assert.Equal(t, false, d2oracle.IsLabelKeyID("x", "y"))
|
||||
assert.Equal(t, false, d2oracle.IsLabelKeyID("x->y", "y"))
|
||||
}
|
||||
|
||||
func TestGetID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, `"y (z)"`, d2oracle.GetID(`y (z)`))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -919,11 +919,12 @@ func (p *parser) parseKey() (k *d2ast.KeyPath) {
|
|||
Start: p.pos,
|
||||
},
|
||||
}
|
||||
defer k.Range.End.From(&p.pos)
|
||||
|
||||
defer func() {
|
||||
if len(k.Path) == 0 {
|
||||
k = nil
|
||||
} else {
|
||||
k.Range.End = k.Path[len(k.Path)-1].Unbox().GetRange().End
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -948,6 +949,9 @@ func (p *parser) parseKey() (k *d2ast.KeyPath) {
|
|||
return k
|
||||
}
|
||||
|
||||
if len(k.Path) == 0 {
|
||||
k.Range.Start = s.GetRange().Start
|
||||
}
|
||||
k.Path = append(k.Path, &sb)
|
||||
|
||||
r, newlines, eof = p.peekNotSpace()
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ func TestParse(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
text string
|
||||
name string
|
||||
text string
|
||||
assert func(t testing.TB, ast *d2ast.Map, err error)
|
||||
|
||||
// exp is in testdata/d2parser/TestParse/${name}.json
|
||||
}{
|
||||
|
|
@ -379,6 +380,18 @@ b-
|
|||
c-
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "whitespace_range",
|
||||
text: ` a -> b -> c `,
|
||||
assert: func(t testing.TB, ast *d2ast.Map, err error) {
|
||||
assert.Equal(t, "1:2", ast.Nodes[0].MapKey.Edges[0].Src.Range.Start.String())
|
||||
assert.Equal(t, "1:3", ast.Nodes[0].MapKey.Edges[0].Src.Range.End.String())
|
||||
assert.Equal(t, "1:7", ast.Nodes[0].MapKey.Edges[0].Dst.Range.Start.String())
|
||||
assert.Equal(t, "1:8", ast.Nodes[0].MapKey.Edges[0].Dst.Range.End.String())
|
||||
assert.Equal(t, "1:12", ast.Nodes[0].MapKey.Edges[1].Dst.Range.Start.String())
|
||||
assert.Equal(t, "1:13", ast.Nodes[0].MapKey.Edges[1].Dst.Range.End.String())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
@ -389,6 +402,10 @@ c-
|
|||
d2Path := fmt.Sprintf("d2/testdata/d2parser/%v.d2", t.Name())
|
||||
ast, err := d2parser.Parse(d2Path, strings.NewReader(tc.text), nil)
|
||||
|
||||
if tc.assert != nil {
|
||||
tc.assert(t, ast, err)
|
||||
}
|
||||
|
||||
got := struct {
|
||||
AST *d2ast.Map `json:"ast"`
|
||||
Err error `json:"err"`
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
|
||||
"oss.terrastruct.com/d2/lib/font"
|
||||
fontlib "oss.terrastruct.com/d2/lib/font"
|
||||
)
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ func (f Font) GetEncodedSubset(corpus string) string {
|
|||
|
||||
fontBuf := make([]byte, len(FontFaces[f]))
|
||||
copy(fontBuf, FontFaces[f])
|
||||
fontBuf = gofpdf.UTF8CutFont(fontBuf, uniqueChars)
|
||||
fontBuf = font.UTF8CutFont(fontBuf, uniqueChars)
|
||||
|
||||
fontBuf, err := fontlib.Sfnt2Woff(fontBuf)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
|
||||
"oss.terrastruct.com/d2/lib/font"
|
||||
"oss.terrastruct.com/util-go/assert"
|
||||
"oss.terrastruct.com/util-go/diff"
|
||||
)
|
||||
|
|
@ -17,7 +16,7 @@ func TestCutFont(t *testing.T) {
|
|||
}
|
||||
fontBuf := make([]byte, len(FontFaces[f]))
|
||||
copy(fontBuf, FontFaces[f])
|
||||
fontBuf = gofpdf.UTF8CutFont(fontBuf, " 1")
|
||||
fontBuf = font.UTF8CutFont(fontBuf, " 1")
|
||||
err := diff.Testdata(filepath.Join("testdata", "d2fonts", "cut"), ".txt", fontBuf)
|
||||
assert.Success(t, err)
|
||||
}
|
||||
|
|
|
|||
BIN
d2renderers/d2fonts/testdata/d2fonts/cut.exp.txt
vendored
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
166
d2renderers/d2sketch/testdata/dots-3d/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
586
d2renderers/d2sketch/testdata/twitter/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 676 KiB After Width: | Height: | Size: 676 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 304 407"><svg id="d2-svg" class="d2-916646398" width="304" height="407" viewBox="-101 -118 304 407"><rect x="-101.000000" y="-118.000000" width="304" height="407" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 304 407"><svg id="d2-svg" class="d2-916646398" width="304" height="407" viewBox="-101 -118 304 407"><rect x="-101.000000" y="-118.000000" width="304" height="407" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.appendix-icon {
|
||||
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
}
|
||||
@font-face {
|
||||
font-family: d2-916646398-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,d09GRgABAAAAAAkcAAoAAAAADnQAAguFAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgXxHXrmNtYXAAAAFUAAAAZQAAAHwB5gImZ2x5ZgAAAbwAAANOAAAD2Mcgs/ZoZWFkAAAFDAAAADYAAAA2G38e1GhoZWEAAAVEAAAAJAAAACQKfwXLaG10eAAABWgAAAAwAAAAMBYfAgdsb2NhAAAFmAAAABoAAAAaByQGRG1heHAAAAW0AAAAIAAAACAAJAD3bmFtZQAABdQAAAMoAAAIKgjwVkFwb3N0AAAI/AAAAB0AAAAg/9EAMgADAioCvAAFAAACigJYAAAASwKKAlgAAAFeADIBKQAAAgsHAwMEAwICBGAAAvcAAAADAAAAAAAAAABBREJPACAAIP//Au7/BgAAA9gBESAAAZ8AAAAAAfAClAAAACAAA3icXMw9DkFBAEbRM2/Gv0IsRGJJCp2EiIJdSERUEttSWMsn0Xm3vMVBURXMNQcsLVSdlbWNrZ29k0vC3zs6J/nknVeeeeSeW64/qd/MVNGpmoGhkbEJXwAAAP//AQAA///xkRf9AAAAeJxkk01sG0UUx9+s17v2ZolZe2fXduJs7bF34nw4xOPdwXUdJ6mJkXBa46pNUdpG5MBX0gSlrhyVA5eckKoiNUKBQ7jADU6cqARIXIAzVJU4geAMQbI4OTZaWxCk3kf//+/93jzwQwNA2BQOwQdBCEEYMADTklqGUUpkzjgnpo9TpMkNIdz79BOaFbNZcerckXVvYwOt3hIOT7fXVzc3/94olXrHXz7q3Ud3HgEIMNXvoJ9QF2JAAMyU7RRcbtskJcnUdVnewBqhRJJ43uWOJGHd+LraOHgokKy1mHbmts5vvLaviFYtEMtELl2w1LXKpeuhJI3iVxPpnb3e72yc7JmRNWU6ETXB60v3O+hX1IUoWAD+lO0Vej0G1iU5aRgsz01J8rGCx4Cs2t7yxe1S7eacKPSeKCvzjjtv3/roCzqTctWFVvPlVqWyVY1kgi5LvhKfQOezzhwAAIKlfgeFha8gNJxKY5pusLzrhX9fLz3Ugn5ZCqsZdf0lgZw+McMI3fbLQz5BRl0IwdhTfBLNu84ADOsGMiq71epupbJTre5UZnO52dzsrFq+27zSKpdbV5p3y+3VxaV6fWlxdcADgB6gLoS9vTGTDUJNeahaW9pXxLG6jceV6DOxZ8fLOjpZy8/7/e+KYjbf+wUQ4H4HfYy6QAfzUO6Z8mBsmhOcwlkY1g1zQsC69OP86/ZyqmIlJxK5+ERp8s2rxTVrOV6IF4v2uXL2DdW2bsTGzIhmRBQ1Xcy+cI1Gr+sGjcZGR0gxd/Hm0KPW76AdoQXmwIbjEIdzhhkm+D+fCG5crta1e+02SagxxYxw9a1rP9yWDg7ufDeVkcQtSR1mjQKgDjqBGACLUGYahueBcyabhNq2989kefTowfGMYihiIBxIHb3/4fFzqqmKQT1IkfBHA09jPI0b/b+aeAbjaaPp5ar9BXSKTryNnbnh3Pe/Bt+osG8kQ3E5HMhMKvI3h7WRsCIGtOCF+5+Zz1/+VhLfRv50Io5+e5xayZAaedwbWbg6NeReAUA/C++ACsAcphHHdTnTGF55r114MbXdbqPddWVcP+22h+/L/Q78CZ/DyL8X5d2RLn1gM2bbjKkOnXScSerAPwAAAP//AQAA//+mJsX8AAAAAQAAAAILhb0aRslfDzz1AAED6AAAAADYXaCEAAAAAN1mLzb+N/7ECG0D8QABAAMAAgAAAAAAAAABAAAD2P7vAAAImP43/jcIbQABAAAAAAAAAAAAAAAAAAAADAKyAFACDwAqAgYAJAEeAEECKwAkAY4AQQG7ABUBfwARAgIADgIJAAwCEABGASwAPQAAACwAZACYALQA4AEAATwBYgGOAb4B1gHsAAAAAQAAAAwAkAAMAGMABwABAAAAAAAAAAAAAAAAAAQAA3icnJTPbhtVFMZ/TmzTCsECRVW6ie6CRZHo2FRJ1TYrh9SKRRQHjwtCQkgTz/iPMp4ZeSYO4QlY8xa8RVc8BM+BWKP5fOzYBdEmipJ8d+75851zvnOBHf5mm0r1IfBHPTFcYa9+bniLB/UTw9u061uGqzyp/Wm4RlibG67zea1n+CPeVn8z/ID96k+GH7JbbRv+mGfVHcOfbDv+Mvwp+7xd4Aq84FfDFXbJDG+xw4+Gt3mExaxUeUTTcI3P2DNcZw/oM6EgZkLCCMeQCSOumBGR4xMxY8KQiBBHhxYxhb4mBEKO0X9+DfApmBEo4pgCR4xPTEDO2CL+Iq+Uc2Uc6jSzuxYFYwIu5HFJQIIjZURKQsSl4hQUZLyiQYOcgfhmFOR45EyI8UiZMaJBlzan9BkzIcfRVqSSmU/KkIJrAuV3ZlF2ZkBEQm6srkgIxdOJXyTvDqc4umSyXY98uhHhSxzfybvklsr2Kzz9ujVmm3mXbALm6mesrsS6udYEx7ot87b4VrjgFe5e/dlk8v4ehfpfKPIFV5p/qEklYpLg3C4tfCnId49xHOncwVdHvqdDnxO6vKGvc4sePVqc0afDa/l26eH4mi5nHMujI7y4a0sxZ/yA4xs6siljR9afxcQifiYzdefiOFMdUzL1vGTuqdZIFd59wuUOpRvqyOUz0B6Vlk7zS7RnASNTRSaGU/VyqY3c+heaIqaqpZzt7X25DXPbveUW35Bqh0u1LjiVk1swet9UvXc0c60fj4CQlAtZDEiZ0qDgRrzPCbgixnGs7p1oSwpaK58yz41UEjEVgw6J4szI9Dcw3fjGfbChe2dvSSj/kunlqqr7ZHHq1e2M3qh7yzvfuhytTaBhU03X1DQQ18S0H2mn1vn78s31uqU85YiUmPBfL8AzPJrsc8AhY2UY6GZur0NTL0STlxyq+ksiWQ2l58giHODxnAMOeMnzd/q4ZOKMi1txWc/d4pgjuhx+UBUL+y5HvF59+/+sv4tpU7U4nq5OL+49xSd3UOsX2rPb97KniZWTmFu02604I2BacnG76zW5x3j/AAAA//8BAAD///S3T1F4nGJgZgCD/+cYjBiwAAAAAAD//wEAAP//LwECAwAAAA==");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 657 KiB After Width: | Height: | Size: 657 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB |
|
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 660 KiB |
|
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 660 KiB |
|
|
@ -642,7 +642,7 @@ func defineShadowFilter(writer io.Writer) {
|
|||
</defs>`)
|
||||
}
|
||||
|
||||
func render3dRect(targetShape d2target.Shape) string {
|
||||
func render3DRect(targetShape d2target.Shape) string {
|
||||
moveTo := func(p d2target.Point) string {
|
||||
return fmt.Sprintf("M%d,%d", p.X+targetShape.Pos.X, p.Y+targetShape.Pos.Y)
|
||||
}
|
||||
|
|
@ -738,7 +738,7 @@ func render3dRect(targetShape d2target.Shape) string {
|
|||
return borderMask + mainShapeRendered + renderedSides + renderedBorder
|
||||
}
|
||||
|
||||
func render3dHexagon(targetShape d2target.Shape) string {
|
||||
func render3DHexagon(targetShape d2target.Shape) string {
|
||||
moveTo := func(p d2target.Point) string {
|
||||
return fmt.Sprintf("M%d,%d", p.X+targetShape.Pos.X, p.Y+targetShape.Pos.Y)
|
||||
}
|
||||
|
|
@ -995,7 +995,7 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
borderRadius = float64(targetShape.BorderRadius)
|
||||
}
|
||||
if targetShape.ThreeDee {
|
||||
fmt.Fprint(writer, render3dRect(targetShape))
|
||||
fmt.Fprint(writer, render3DRect(targetShape))
|
||||
} else {
|
||||
if !targetShape.DoubleBorder {
|
||||
if targetShape.Multiple {
|
||||
|
|
@ -1088,7 +1088,7 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
}
|
||||
case d2target.ShapeHexagon:
|
||||
if targetShape.ThreeDee {
|
||||
fmt.Fprint(writer, render3dHexagon(targetShape))
|
||||
fmt.Fprint(writer, render3DHexagon(targetShape))
|
||||
} else {
|
||||
if targetShape.Multiple {
|
||||
multiplePathData := shape.NewShape(shapeType, geo.NewBox(multipleTL, width, height)).GetSVGPathData()
|
||||
|
|
@ -1186,7 +1186,19 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
labelPosition := label.Position(targetShape.LabelPosition)
|
||||
var box *geo.Box
|
||||
if labelPosition.IsOutside() {
|
||||
box = s.GetBox()
|
||||
box = s.GetBox().Copy()
|
||||
// if it is 3d/multiple, place label using box around those
|
||||
if targetShape.ThreeDee {
|
||||
offsetY := d2target.THREE_DEE_OFFSET
|
||||
if targetShape.Type == d2target.ShapeHexagon {
|
||||
offsetY /= 2
|
||||
}
|
||||
box.TopLeft.Y -= float64(offsetY)
|
||||
box.Width += d2target.THREE_DEE_OFFSET
|
||||
} else if targetShape.Multiple {
|
||||
box.TopLeft.Y -= d2target.MULTIPLE_OFFSET
|
||||
box.Width += d2target.MULTIPLE_OFFSET
|
||||
}
|
||||
} else {
|
||||
box = s.GetInnerBox()
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
|
@ -22,6 +22,8 @@ const (
|
|||
DEFAULT_ICON_SIZE = 32
|
||||
MAX_ICON_SIZE = 64
|
||||
|
||||
SHADOW_SIZE_X = 3
|
||||
SHADOW_SIZE_Y = 5
|
||||
THREE_DEE_OFFSET = 15
|
||||
MULTIPLE_OFFSET = 10
|
||||
|
||||
|
|
@ -171,9 +173,17 @@ func (diagram Diagram) BoundingBox() (topLeft, bottomRight Point) {
|
|||
y1 = go2.Min(y1, targetShape.Pos.Y-targetShape.StrokeWidth-16)
|
||||
x2 = go2.Max(x2, targetShape.Pos.X+targetShape.StrokeWidth+targetShape.Width+16)
|
||||
}
|
||||
if targetShape.Shadow {
|
||||
y2 = go2.Max(y2, targetShape.Pos.Y+targetShape.Height+int(math.Ceil(float64(targetShape.StrokeWidth)/2.))+SHADOW_SIZE_Y)
|
||||
x2 = go2.Max(x2, targetShape.Pos.X+targetShape.Width+int(math.Ceil(float64(targetShape.StrokeWidth)/2.))+SHADOW_SIZE_X)
|
||||
}
|
||||
|
||||
if targetShape.ThreeDee {
|
||||
y1 = go2.Min(y1, targetShape.Pos.Y-THREE_DEE_OFFSET-targetShape.StrokeWidth)
|
||||
offsetY := THREE_DEE_OFFSET
|
||||
if targetShape.Type == ShapeHexagon {
|
||||
offsetY /= 2
|
||||
}
|
||||
y1 = go2.Min(y1, targetShape.Pos.Y-offsetY-targetShape.StrokeWidth)
|
||||
x2 = go2.Max(x2, targetShape.Pos.X+THREE_DEE_OFFSET+targetShape.Width+targetShape.StrokeWidth)
|
||||
}
|
||||
if targetShape.Multiple {
|
||||
|
|
@ -853,6 +863,8 @@ func init() {
|
|||
for k, v := range DSL_SHAPE_TO_SHAPE_TYPE {
|
||||
SHAPE_TYPE_TO_DSL_SHAPE[v] = k
|
||||
}
|
||||
// SQUARE_TYPE is defined twice in the map, make sure it doesn't get set to the empty string one
|
||||
SHAPE_TYPE_TO_DSL_SHAPE[shape.SQUARE_TYPE] = ShapeRectangle
|
||||
}
|
||||
|
||||
func GetIconSize(box *geo.Box, position string) int {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func main() {
|
|||
// Create a shape with the ID, "cat"
|
||||
graph, _, _ = d2oracle.Create(graph, "cat")
|
||||
// Move the shape "meow" inside the container "cat"
|
||||
graph, _ = d2oracle.Move(graph, "meow", "cat.meow")
|
||||
graph, _ = d2oracle.Move(graph, "meow", "cat.meow", false)
|
||||
// Prints formatted D2 script
|
||||
fmt.Print(d2format.Format(graph.AST))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -76,6 +77,30 @@ func TestCLI_E2E(t *testing.T) {
|
|||
assert.TestdataDir(t, filepath.Join(dir, "empty-layer"))
|
||||
},
|
||||
},
|
||||
{
|
||||
// Skip the empty base board so the animation doesn't show blank for 1400ms
|
||||
name: "empty-base",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
writeFile(t, dir, "empty-base.d2", `steps: {
|
||||
1: {
|
||||
a -> b
|
||||
}
|
||||
2: {
|
||||
b -> d
|
||||
c -> d
|
||||
}
|
||||
3: {
|
||||
d -> e
|
||||
}
|
||||
}`)
|
||||
|
||||
err := runTestMain(t, ctx, dir, env, "--animate-interval=1400", "empty-base.d2")
|
||||
assert.Success(t, err)
|
||||
svg := readFile(t, dir, "empty-base.svg")
|
||||
assert.Testdata(t, ".svg", svg)
|
||||
assert.Equal(t, 3, getNumBoards(string(svg)))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "animation",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
|
|
@ -447,3 +472,9 @@ func removeD2Files(tb testing.TB, dir string) {
|
|||
func testdataIgnoreDiff(tb testing.TB, ext string, got []byte) {
|
||||
_ = diff.Testdata(filepath.Join("testdata", tb.Name()), ext, got)
|
||||
}
|
||||
|
||||
// getNumBoards gets the number of boards in an SVG file through a non-robust pattern search
|
||||
// If the renderer changes, this must change
|
||||
func getNumBoards(svg string) int {
|
||||
return strings.Count(svg, `class="d2`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-855222762" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-855222762" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-855222762 .text-bold {
|
||||
font-family: "d2-855222762-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-855222762-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 514 665"><svg id="d2-svg" width="514" height="665" viewBox="-206 -166 514 665"><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 514 665"><svg id="d2-svg" width="514" height="665" viewBox="-206 -166 514 665"><style type="text/css"><![CDATA[
|
||||
.d2-508224771 .text {
|
||||
font-family: "d2-508224771-font-regular";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-508224771-font-regular;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}
|
||||
.d2-508224771 .text-bold {
|
||||
font-family: "d2-508224771-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-508224771-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-855222762" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-855222762" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-855222762 .text-bold {
|
||||
font-family: "d2-855222762-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-855222762-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
128
e2etests-cli/testdata/TestCLI_E2E/empty-base.exp.svg
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 368 766"><svg id="d2-svg" width="368" height="766" viewBox="-101 -101 368 766"><style type="text/css"><![CDATA[
|
||||
.d2-1644916896 .text-bold {
|
||||
font-family: "d2-1644916896-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-1644916896-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.connection {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.blend {
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.d2-1644916896 .fill-N1{fill:#0A0F25;}
|
||||
.d2-1644916896 .fill-N2{fill:#676C7E;}
|
||||
.d2-1644916896 .fill-N3{fill:#9499AB;}
|
||||
.d2-1644916896 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-1644916896 .fill-N5{fill:#DEE1EB;}
|
||||
.d2-1644916896 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-1644916896 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-1644916896 .fill-B1{fill:#0D32B2;}
|
||||
.d2-1644916896 .fill-B2{fill:#0D32B2;}
|
||||
.d2-1644916896 .fill-B3{fill:#E3E9FD;}
|
||||
.d2-1644916896 .fill-B4{fill:#E3E9FD;}
|
||||
.d2-1644916896 .fill-B5{fill:#EDF0FD;}
|
||||
.d2-1644916896 .fill-B6{fill:#F7F8FE;}
|
||||
.d2-1644916896 .fill-AA2{fill:#4A6FF3;}
|
||||
.d2-1644916896 .fill-AA4{fill:#EDF0FD;}
|
||||
.d2-1644916896 .fill-AA5{fill:#F7F8FE;}
|
||||
.d2-1644916896 .fill-AB4{fill:#EDF0FD;}
|
||||
.d2-1644916896 .fill-AB5{fill:#F7F8FE;}
|
||||
.d2-1644916896 .stroke-N1{stroke:#0A0F25;}
|
||||
.d2-1644916896 .stroke-N2{stroke:#676C7E;}
|
||||
.d2-1644916896 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-1644916896 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-1644916896 .stroke-N5{stroke:#DEE1EB;}
|
||||
.d2-1644916896 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-1644916896 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-1644916896 .stroke-B1{stroke:#0D32B2;}
|
||||
.d2-1644916896 .stroke-B2{stroke:#0D32B2;}
|
||||
.d2-1644916896 .stroke-B3{stroke:#E3E9FD;}
|
||||
.d2-1644916896 .stroke-B4{stroke:#E3E9FD;}
|
||||
.d2-1644916896 .stroke-B5{stroke:#EDF0FD;}
|
||||
.d2-1644916896 .stroke-B6{stroke:#F7F8FE;}
|
||||
.d2-1644916896 .stroke-AA2{stroke:#4A6FF3;}
|
||||
.d2-1644916896 .stroke-AA4{stroke:#EDF0FD;}
|
||||
.d2-1644916896 .stroke-AA5{stroke:#F7F8FE;}
|
||||
.d2-1644916896 .stroke-AB4{stroke:#EDF0FD;}
|
||||
.d2-1644916896 .stroke-AB5{stroke:#F7F8FE;}
|
||||
.d2-1644916896 .background-color-N1{background-color:#0A0F25;}
|
||||
.d2-1644916896 .background-color-N2{background-color:#676C7E;}
|
||||
.d2-1644916896 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-1644916896 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-1644916896 .background-color-N5{background-color:#DEE1EB;}
|
||||
.d2-1644916896 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-1644916896 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-1644916896 .background-color-B1{background-color:#0D32B2;}
|
||||
.d2-1644916896 .background-color-B2{background-color:#0D32B2;}
|
||||
.d2-1644916896 .background-color-B3{background-color:#E3E9FD;}
|
||||
.d2-1644916896 .background-color-B4{background-color:#E3E9FD;}
|
||||
.d2-1644916896 .background-color-B5{background-color:#EDF0FD;}
|
||||
.d2-1644916896 .background-color-B6{background-color:#F7F8FE;}
|
||||
.d2-1644916896 .background-color-AA2{background-color:#4A6FF3;}
|
||||
.d2-1644916896 .background-color-AA4{background-color:#EDF0FD;}
|
||||
.d2-1644916896 .background-color-AA5{background-color:#F7F8FE;}
|
||||
.d2-1644916896 .background-color-AB4{background-color:#EDF0FD;}
|
||||
.d2-1644916896 .background-color-AB5{background-color:#F7F8FE;}
|
||||
.d2-1644916896 .color-N1{color:#0A0F25;}
|
||||
.d2-1644916896 .color-N2{color:#676C7E;}
|
||||
.d2-1644916896 .color-N3{color:#9499AB;}
|
||||
.d2-1644916896 .color-N4{color:#CFD2DD;}
|
||||
.d2-1644916896 .color-N5{color:#DEE1EB;}
|
||||
.d2-1644916896 .color-N6{color:#EEF1F8;}
|
||||
.d2-1644916896 .color-N7{color:#FFFFFF;}
|
||||
.d2-1644916896 .color-B1{color:#0D32B2;}
|
||||
.d2-1644916896 .color-B2{color:#0D32B2;}
|
||||
.d2-1644916896 .color-B3{color:#E3E9FD;}
|
||||
.d2-1644916896 .color-B4{color:#E3E9FD;}
|
||||
.d2-1644916896 .color-B5{color:#EDF0FD;}
|
||||
.d2-1644916896 .color-B6{color:#F7F8FE;}
|
||||
.d2-1644916896 .color-AA2{color:#4A6FF3;}
|
||||
.d2-1644916896 .color-AA4{color:#EDF0FD;}
|
||||
.d2-1644916896 .color-AA5{color:#F7F8FE;}
|
||||
.d2-1644916896 .color-AB4{color:#EDF0FD;}
|
||||
.d2-1644916896 .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);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><style type="text/css"><![CDATA[@keyframes d2Transition-d2-1644916896-0 {
|
||||
0%, 0.000000% {
|
||||
opacity: 0;
|
||||
}
|
||||
0.000000%, 33.309524% {
|
||||
opacity: 1;
|
||||
}
|
||||
33.333333%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}@keyframes d2Transition-d2-1644916896-1 {
|
||||
0%, 33.309524% {
|
||||
opacity: 0;
|
||||
}
|
||||
33.333333%, 66.642857% {
|
||||
opacity: 1;
|
||||
}
|
||||
66.666667%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}@keyframes d2Transition-d2-1644916896-2 {
|
||||
0%, 66.642857% {
|
||||
opacity: 0;
|
||||
}
|
||||
66.666667%, 100.000000% {
|
||||
opacity: 1;
|
||||
}
|
||||
}]]></style><g style="animation: d2Transition-d2-1644916896-0 4200ms infinite" class="d2-1644916896" width="255" height="434" viewBox="-101 -101 255 434"><rect x="-101.000000" y="-101.000000" width="255.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="a"><g class="shape" ><rect x="0.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">a</text></g><g id="b"><g class="shape" ><rect x="0.000000" y="166.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">b</text></g><g id="(a -> b)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 26.500000 68.000000 C 26.500000 106.000000 26.500000 126.000000 26.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-746933975)" /></g><mask id="d2-746933975" maskUnits="userSpaceOnUse" x="-101" y="-101" width="255" height="434">
|
||||
<rect x="-101" y="-101" width="255" height="434" fill="white"></rect>
|
||||
|
||||
</mask></g><g style="animation: d2Transition-d2-1644916896-1 4200ms infinite" class="d2-1644916896" width="368" height="600" viewBox="-101 -101 368 600"><rect x="-101.000000" y="-101.000000" width="368.000000" height="600.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="a"><g class="shape" ><rect x="0.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">a</text></g><g id="b"><g class="shape" ><rect x="0.000000" y="166.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">b</text></g><g id="d"><g class="shape" ><rect x="56.000000" y="332.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="83.000000" y="370.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">d</text></g><g id="c"><g class="shape" ><rect x="113.000000" y="166.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="139.500000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">c</text></g><g id="(a -> b)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 26.500000 68.000000 C 26.500000 106.000000 26.500000 126.000000 26.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-919984524)" /></g><g id="(b -> d)[0]"><path d="M 26.500000 234.000000 C 26.500000 272.000000 33.299999 292.000000 58.250760 328.692294" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-919984524)" /></g><g id="(c -> d)[0]"><path d="M 139.500000 234.000000 C 139.500000 272.000000 132.699997 292.000000 107.749240 328.692294" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-919984524)" /></g><mask id="d2-919984524" maskUnits="userSpaceOnUse" x="-101" y="-101" width="368" height="600">
|
||||
<rect x="-101" y="-101" width="368" height="600" fill="white"></rect>
|
||||
|
||||
</mask></g><g style="animation: d2Transition-d2-1644916896-2 4200ms infinite" class="d2-1644916896" width="368" height="766" viewBox="-101 -101 368 766"><rect x="-101.000000" y="-101.000000" width="368.000000" height="766.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="a"><g class="shape" ><rect x="0.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">a</text></g><g id="b"><g class="shape" ><rect x="0.000000" y="166.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="26.500000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">b</text></g><g id="d"><g class="shape" ><rect x="56.000000" y="332.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="83.000000" y="370.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">d</text></g><g id="c"><g class="shape" ><rect x="113.000000" y="166.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="139.500000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">c</text></g><g id="e"><g class="shape" ><rect x="57.000000" y="498.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="83.500000" y="536.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">e</text></g><g id="(a -> b)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 26.500000 68.000000 C 26.500000 106.000000 26.500000 126.000000 26.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-348119987)" /></g><g id="(b -> d)[0]"><path d="M 26.500000 234.000000 C 26.500000 272.000000 33.299999 292.000000 58.250760 328.692294" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-348119987)" /></g><g id="(c -> d)[0]"><path d="M 139.500000 234.000000 C 139.500000 272.000000 132.699997 292.000000 107.749240 328.692294" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-348119987)" /></g><g id="(d -> e)[0]"><path d="M 83.000000 400.000000 C 83.000000 438.000000 83.000000 458.000000 83.000000 494.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-348119987)" /></g><mask id="d2-348119987" maskUnits="userSpaceOnUse" x="-101" y="-101" width="368" height="766">
|
||||
<rect x="-101" y="-101" width="368" height="766" fill="white"></rect>
|
||||
|
||||
</mask></g></svg></svg>
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 200 200"><svg id="d2-svg" class="d2-121760133" width="200" height="200" viewBox="-100 -100 200 200"><rect x="-100.000000" y="-100.000000" width="200.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[]]></style><style type="text/css"><![CDATA[.shape {
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 200 200"><svg id="d2-svg" class="d2-121760133" width="200" height="200" viewBox="-100 -100 200 200"><rect x="-100.000000" y="-100.000000" width="200.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-803513315" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-803513315 .text-bold {
|
||||
font-family: "d2-803513315-font-bold";
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-1380559207" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-1380559207 .text-bold {
|
||||
font-family: "d2-1380559207-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-803513315-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
font-family: d2-1380559207-font-bold;
|
||||
src: url("data:application/font-woff;base64,d09GRgABAAAAAAZwAAoAAAAACywAAguFAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgXxHXrmNtYXAAAAFUAAAAMgAAADIADQC0Z2x5ZgAAAYgAAAEQAAABEBXyvOFoZWFkAAACmAAAADYAAAA2G38e1GhoZWEAAALQAAAAJAAAACQKfwXCaG10eAAAAvQAAAAMAAAADAa9AGpsb2NhAAADAAAAAAgAAAAIAFgAtG1heHAAAAMIAAAAIAAAACAAGwD3bmFtZQAAAygAAAMoAAAIKgjwVkFwb3N0AAAGUAAAAB0AAAAg/9EAMgADAioCvAAFAAACigJYAAAASwKKAlgAAAFeADIBKQAAAgsHAwMEAwICBGAAAvcAAAADAAAAAAAAAABBREJPACAAIP//Au7/BgAAA9gBESAAAZ8AAAAAAfAClAAAACAAAwAAAAEAAwABAAAADAAEACYAAAAEAAQAAQAAAHn//wAAAHj///+JAAEAAAAAAAEAAgAAAAAABQBQAAACYgKUAAMACQAPABIAFQAAMxEhESUzJycjBzczNzcjFwM3JwERB1ACEv6lpCcpBCkpBCogmB96X18BTV4ClP1sW01iYvZfOzv+nrm6/o0Bc7oAAAEADgAAAfQB8AAZAAAzEyczFxYWFzM2Njc3MwcXIycmJicjBgYHBw6Yj54sChYKBAgSCCKYkJmeMAwXDAQJFAknAQLuUBUrFRUrFVD/8VIVLBUVKxZSAAABAAz/PgH9AfAAGwAAFyImJzcWFjMyNjc3AzMXFhYXMzY2NzczAw4CeBYhDxoHEgglKAoHv5RHCxIKBAgRCTyNrBc4T8IGBHABBSQdGgHj1SJGJSNHI9X+Cz5VKgAAAAABAAAAAguFT5ZgD18PPPUAAQPoAAAAANhdoIQAAAAA3WYvNv43/sQIbQPxAAEAAwACAAAAAAAAAAEAAAPY/u8AAAiY/jf+NwhtAAEAAAAAAAAAAAAAAAAAAAADArIAUAICAA4CCQAMAAAALABYAIgAAQAAAAMAkAAMAGMABwABAAAAAAAAAAAAAAAAAAQAA3icnJTPbhtVFMZ/TmzTCsECRVW6ie6CRZHo2FRJ1TYrh9SKRRQHjwtCQkgTz/iPMp4ZeSYO4QlY8xa8RVc8BM+BWKP5fOzYBdEmipJ8d+75851zvnOBHf5mm0r1IfBHPTFcYa9+bniLB/UTw9u061uGqzyp/Wm4RlibG67zea1n+CPeVn8z/ID96k+GH7JbbRv+mGfVHcOfbDv+Mvwp+7xd4Aq84FfDFXbJDG+xw4+Gt3mExaxUeUTTcI3P2DNcZw/oM6EgZkLCCMeQCSOumBGR4xMxY8KQiBBHhxYxhb4mBEKO0X9+DfApmBEo4pgCR4xPTEDO2CL+Iq+Uc2Uc6jSzuxYFYwIu5HFJQIIjZURKQsSl4hQUZLyiQYOcgfhmFOR45EyI8UiZMaJBlzan9BkzIcfRVqSSmU/KkIJrAuV3ZlF2ZkBEQm6srkgIxdOJXyTvDqc4umSyXY98uhHhSxzfybvklsr2Kzz9ujVmm3mXbALm6mesrsS6udYEx7ot87b4VrjgFe5e/dlk8v4ehfpfKPIFV5p/qEklYpLg3C4tfCnId49xHOncwVdHvqdDnxO6vKGvc4sePVqc0afDa/l26eH4mi5nHMujI7y4a0sxZ/yA4xs6siljR9afxcQifiYzdefiOFMdUzL1vGTuqdZIFd59wuUOpRvqyOUz0B6Vlk7zS7RnASNTRSaGU/VyqY3c+heaIqaqpZzt7X25DXPbveUW35Bqh0u1LjiVk1swet9UvXc0c60fj4CQlAtZDEiZ0qDgRrzPCbgixnGs7p1oSwpaK58yz41UEjEVgw6J4szI9Dcw3fjGfbChe2dvSSj/kunlqqr7ZHHq1e2M3qh7yzvfuhytTaBhU03X1DQQ18S0H2mn1vn78s31uqU85YiUmPBfL8AzPJrsc8AhY2UY6GZur0NTL0STlxyq+ksiWQ2l58giHODxnAMOeMnzd/q4ZOKMi1txWc/d4pgjuhx+UBUL+y5HvF59+/+sv4tpU7U4nq5OL+49xSd3UOsX2rPb97KniZWTmFu02604I2BacnG76zW5x3j/AAAA//8BAAD///S3T1F4nGJgZgCD/+cYjBiwAAAAAAD//wEAAP//LwECAwAAAA==");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
@ -18,78 +18,78 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.d2-803513315 .fill-N1{fill:#0A0F25;}
|
||||
.d2-803513315 .fill-N2{fill:#676C7E;}
|
||||
.d2-803513315 .fill-N3{fill:#9499AB;}
|
||||
.d2-803513315 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-803513315 .fill-N5{fill:#DEE1EB;}
|
||||
.d2-803513315 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-803513315 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-803513315 .fill-B1{fill:#0D32B2;}
|
||||
.d2-803513315 .fill-B2{fill:#0D32B2;}
|
||||
.d2-803513315 .fill-B3{fill:#E3E9FD;}
|
||||
.d2-803513315 .fill-B4{fill:#E3E9FD;}
|
||||
.d2-803513315 .fill-B5{fill:#EDF0FD;}
|
||||
.d2-803513315 .fill-B6{fill:#F7F8FE;}
|
||||
.d2-803513315 .fill-AA2{fill:#4A6FF3;}
|
||||
.d2-803513315 .fill-AA4{fill:#EDF0FD;}
|
||||
.d2-803513315 .fill-AA5{fill:#F7F8FE;}
|
||||
.d2-803513315 .fill-AB4{fill:#EDF0FD;}
|
||||
.d2-803513315 .fill-AB5{fill:#F7F8FE;}
|
||||
.d2-803513315 .stroke-N1{stroke:#0A0F25;}
|
||||
.d2-803513315 .stroke-N2{stroke:#676C7E;}
|
||||
.d2-803513315 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-803513315 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-803513315 .stroke-N5{stroke:#DEE1EB;}
|
||||
.d2-803513315 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-803513315 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-803513315 .stroke-B1{stroke:#0D32B2;}
|
||||
.d2-803513315 .stroke-B2{stroke:#0D32B2;}
|
||||
.d2-803513315 .stroke-B3{stroke:#E3E9FD;}
|
||||
.d2-803513315 .stroke-B4{stroke:#E3E9FD;}
|
||||
.d2-803513315 .stroke-B5{stroke:#EDF0FD;}
|
||||
.d2-803513315 .stroke-B6{stroke:#F7F8FE;}
|
||||
.d2-803513315 .stroke-AA2{stroke:#4A6FF3;}
|
||||
.d2-803513315 .stroke-AA4{stroke:#EDF0FD;}
|
||||
.d2-803513315 .stroke-AA5{stroke:#F7F8FE;}
|
||||
.d2-803513315 .stroke-AB4{stroke:#EDF0FD;}
|
||||
.d2-803513315 .stroke-AB5{stroke:#F7F8FE;}
|
||||
.d2-803513315 .background-color-N1{background-color:#0A0F25;}
|
||||
.d2-803513315 .background-color-N2{background-color:#676C7E;}
|
||||
.d2-803513315 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-803513315 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-803513315 .background-color-N5{background-color:#DEE1EB;}
|
||||
.d2-803513315 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-803513315 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-803513315 .background-color-B1{background-color:#0D32B2;}
|
||||
.d2-803513315 .background-color-B2{background-color:#0D32B2;}
|
||||
.d2-803513315 .background-color-B3{background-color:#E3E9FD;}
|
||||
.d2-803513315 .background-color-B4{background-color:#E3E9FD;}
|
||||
.d2-803513315 .background-color-B5{background-color:#EDF0FD;}
|
||||
.d2-803513315 .background-color-B6{background-color:#F7F8FE;}
|
||||
.d2-803513315 .background-color-AA2{background-color:#4A6FF3;}
|
||||
.d2-803513315 .background-color-AA4{background-color:#EDF0FD;}
|
||||
.d2-803513315 .background-color-AA5{background-color:#F7F8FE;}
|
||||
.d2-803513315 .background-color-AB4{background-color:#EDF0FD;}
|
||||
.d2-803513315 .background-color-AB5{background-color:#F7F8FE;}
|
||||
.d2-803513315 .color-N1{color:#0A0F25;}
|
||||
.d2-803513315 .color-N2{color:#676C7E;}
|
||||
.d2-803513315 .color-N3{color:#9499AB;}
|
||||
.d2-803513315 .color-N4{color:#CFD2DD;}
|
||||
.d2-803513315 .color-N5{color:#DEE1EB;}
|
||||
.d2-803513315 .color-N6{color:#EEF1F8;}
|
||||
.d2-803513315 .color-N7{color:#FFFFFF;}
|
||||
.d2-803513315 .color-B1{color:#0D32B2;}
|
||||
.d2-803513315 .color-B2{color:#0D32B2;}
|
||||
.d2-803513315 .color-B3{color:#E3E9FD;}
|
||||
.d2-803513315 .color-B4{color:#E3E9FD;}
|
||||
.d2-803513315 .color-B5{color:#EDF0FD;}
|
||||
.d2-803513315 .color-B6{color:#F7F8FE;}
|
||||
.d2-803513315 .color-AA2{color:#4A6FF3;}
|
||||
.d2-803513315 .color-AA4{color:#EDF0FD;}
|
||||
.d2-803513315 .color-AA5{color:#F7F8FE;}
|
||||
.d2-803513315 .color-AB4{color:#EDF0FD;}
|
||||
.d2-803513315 .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);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g id="x"><g class="shape" ><rect x="1.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g><g id="y"><g class="shape" ><rect x="0.000000" y="166.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g><g id="(x -> y)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 27.000000 68.000000 C 27.000000 106.000000 27.000000 126.000000 27.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-803513315)" /></g><mask id="d2-803513315" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||
.d2-1380559207 .fill-N1{fill:#0A0F25;}
|
||||
.d2-1380559207 .fill-N2{fill:#676C7E;}
|
||||
.d2-1380559207 .fill-N3{fill:#9499AB;}
|
||||
.d2-1380559207 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-1380559207 .fill-N5{fill:#DEE1EB;}
|
||||
.d2-1380559207 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-1380559207 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-1380559207 .fill-B1{fill:#0D32B2;}
|
||||
.d2-1380559207 .fill-B2{fill:#0D32B2;}
|
||||
.d2-1380559207 .fill-B3{fill:#E3E9FD;}
|
||||
.d2-1380559207 .fill-B4{fill:#E3E9FD;}
|
||||
.d2-1380559207 .fill-B5{fill:#EDF0FD;}
|
||||
.d2-1380559207 .fill-B6{fill:#F7F8FE;}
|
||||
.d2-1380559207 .fill-AA2{fill:#4A6FF3;}
|
||||
.d2-1380559207 .fill-AA4{fill:#EDF0FD;}
|
||||
.d2-1380559207 .fill-AA5{fill:#F7F8FE;}
|
||||
.d2-1380559207 .fill-AB4{fill:#EDF0FD;}
|
||||
.d2-1380559207 .fill-AB5{fill:#F7F8FE;}
|
||||
.d2-1380559207 .stroke-N1{stroke:#0A0F25;}
|
||||
.d2-1380559207 .stroke-N2{stroke:#676C7E;}
|
||||
.d2-1380559207 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-1380559207 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-1380559207 .stroke-N5{stroke:#DEE1EB;}
|
||||
.d2-1380559207 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-1380559207 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-1380559207 .stroke-B1{stroke:#0D32B2;}
|
||||
.d2-1380559207 .stroke-B2{stroke:#0D32B2;}
|
||||
.d2-1380559207 .stroke-B3{stroke:#E3E9FD;}
|
||||
.d2-1380559207 .stroke-B4{stroke:#E3E9FD;}
|
||||
.d2-1380559207 .stroke-B5{stroke:#EDF0FD;}
|
||||
.d2-1380559207 .stroke-B6{stroke:#F7F8FE;}
|
||||
.d2-1380559207 .stroke-AA2{stroke:#4A6FF3;}
|
||||
.d2-1380559207 .stroke-AA4{stroke:#EDF0FD;}
|
||||
.d2-1380559207 .stroke-AA5{stroke:#F7F8FE;}
|
||||
.d2-1380559207 .stroke-AB4{stroke:#EDF0FD;}
|
||||
.d2-1380559207 .stroke-AB5{stroke:#F7F8FE;}
|
||||
.d2-1380559207 .background-color-N1{background-color:#0A0F25;}
|
||||
.d2-1380559207 .background-color-N2{background-color:#676C7E;}
|
||||
.d2-1380559207 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-1380559207 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-1380559207 .background-color-N5{background-color:#DEE1EB;}
|
||||
.d2-1380559207 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-1380559207 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-1380559207 .background-color-B1{background-color:#0D32B2;}
|
||||
.d2-1380559207 .background-color-B2{background-color:#0D32B2;}
|
||||
.d2-1380559207 .background-color-B3{background-color:#E3E9FD;}
|
||||
.d2-1380559207 .background-color-B4{background-color:#E3E9FD;}
|
||||
.d2-1380559207 .background-color-B5{background-color:#EDF0FD;}
|
||||
.d2-1380559207 .background-color-B6{background-color:#F7F8FE;}
|
||||
.d2-1380559207 .background-color-AA2{background-color:#4A6FF3;}
|
||||
.d2-1380559207 .background-color-AA4{background-color:#EDF0FD;}
|
||||
.d2-1380559207 .background-color-AA5{background-color:#F7F8FE;}
|
||||
.d2-1380559207 .background-color-AB4{background-color:#EDF0FD;}
|
||||
.d2-1380559207 .background-color-AB5{background-color:#F7F8FE;}
|
||||
.d2-1380559207 .color-N1{color:#0A0F25;}
|
||||
.d2-1380559207 .color-N2{color:#676C7E;}
|
||||
.d2-1380559207 .color-N3{color:#9499AB;}
|
||||
.d2-1380559207 .color-N4{color:#CFD2DD;}
|
||||
.d2-1380559207 .color-N5{color:#DEE1EB;}
|
||||
.d2-1380559207 .color-N6{color:#EEF1F8;}
|
||||
.d2-1380559207 .color-N7{color:#FFFFFF;}
|
||||
.d2-1380559207 .color-B1{color:#0D32B2;}
|
||||
.d2-1380559207 .color-B2{color:#0D32B2;}
|
||||
.d2-1380559207 .color-B3{color:#E3E9FD;}
|
||||
.d2-1380559207 .color-B4{color:#E3E9FD;}
|
||||
.d2-1380559207 .color-B5{color:#EDF0FD;}
|
||||
.d2-1380559207 .color-B6{color:#F7F8FE;}
|
||||
.d2-1380559207 .color-AA2{color:#4A6FF3;}
|
||||
.d2-1380559207 .color-AA4{color:#EDF0FD;}
|
||||
.d2-1380559207 .color-AA5{color:#F7F8FE;}
|
||||
.d2-1380559207 .color-AB4{color:#EDF0FD;}
|
||||
.d2-1380559207 .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);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g id="x"><g class="shape" ><rect x="1.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g><g id="y"><g class="shape" ><rect x="0.000000" y="166.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g><g id="(x -> y)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 27.000000 68.000000 C 27.000000 106.000000 27.000000 126.000000 27.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1380559207)" /></g><mask id="d2-1380559207" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||
<rect x="-101" y="-101" width="256" height="434" fill="white"></rect>
|
||||
|
||||
</mask></svg></svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 469 268"><svg id="d2-svg" class="d2-1194982555" width="469" height="268" viewBox="-101 -101 469 268"><rect x="-101.000000" y="-101.000000" width="469.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 469 268"><svg id="d2-svg" class="d2-1194982555" width="469" height="268" viewBox="-101 -101 469 268"><rect x="-101.000000" y="-101.000000" width="469.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-1194982555 .text-bold {
|
||||
font-family: "d2-1194982555-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-1194982555-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 558 268"><svg id="d2-svg" class="d2-4059799029" width="558" height="268" viewBox="-101 -101 558 268"><rect x="-101.000000" y="-101.000000" width="558.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 558 268"><svg id="d2-svg" class="d2-4059799029" width="558" height="268" viewBox="-101 -101 558 268"><rect x="-101.000000" y="-101.000000" width="558.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-4059799029 .text-bold {
|
||||
font-family: "d2-4059799029-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-4059799029-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 578 268"><svg id="d2-svg" class="d2-1066622782" width="578" height="268" viewBox="-101 -101 578 268"><rect x="-101.000000" y="-101.000000" width="578.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 578 268"><svg id="d2-svg" class="d2-1066622782" width="578" height="268" viewBox="-101 -101 578 268"><rect x="-101.000000" y="-101.000000" width="578.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-1066622782 .text-bold {
|
||||
font-family: "d2-1066622782-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-1066622782-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-2065426295" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-2065426295 .text-bold {
|
||||
font-family: "d2-2065426295-font-bold";
|
||||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.4.2-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-2561523587" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-2561523587 .text-bold {
|
||||
font-family: "d2-2561523587-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-2065426295-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
font-family: d2-2561523587-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
|
|
@ -18,78 +18,78 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.d2-2065426295 .fill-N1{fill:#0A0F25;}
|
||||
.d2-2065426295 .fill-N2{fill:#676C7E;}
|
||||
.d2-2065426295 .fill-N3{fill:#9499AB;}
|
||||
.d2-2065426295 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-2065426295 .fill-N5{fill:#DEE1EB;}
|
||||
.d2-2065426295 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-2065426295 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-2065426295 .fill-B1{fill:#0D32B2;}
|
||||
.d2-2065426295 .fill-B2{fill:#0D32B2;}
|
||||
.d2-2065426295 .fill-B3{fill:#E3E9FD;}
|
||||
.d2-2065426295 .fill-B4{fill:#E3E9FD;}
|
||||
.d2-2065426295 .fill-B5{fill:#EDF0FD;}
|
||||
.d2-2065426295 .fill-B6{fill:#F7F8FE;}
|
||||
.d2-2065426295 .fill-AA2{fill:#4A6FF3;}
|
||||
.d2-2065426295 .fill-AA4{fill:#EDF0FD;}
|
||||
.d2-2065426295 .fill-AA5{fill:#F7F8FE;}
|
||||
.d2-2065426295 .fill-AB4{fill:#EDF0FD;}
|
||||
.d2-2065426295 .fill-AB5{fill:#F7F8FE;}
|
||||
.d2-2065426295 .stroke-N1{stroke:#0A0F25;}
|
||||
.d2-2065426295 .stroke-N2{stroke:#676C7E;}
|
||||
.d2-2065426295 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-2065426295 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-2065426295 .stroke-N5{stroke:#DEE1EB;}
|
||||
.d2-2065426295 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-2065426295 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-2065426295 .stroke-B1{stroke:#0D32B2;}
|
||||
.d2-2065426295 .stroke-B2{stroke:#0D32B2;}
|
||||
.d2-2065426295 .stroke-B3{stroke:#E3E9FD;}
|
||||
.d2-2065426295 .stroke-B4{stroke:#E3E9FD;}
|
||||
.d2-2065426295 .stroke-B5{stroke:#EDF0FD;}
|
||||
.d2-2065426295 .stroke-B6{stroke:#F7F8FE;}
|
||||
.d2-2065426295 .stroke-AA2{stroke:#4A6FF3;}
|
||||
.d2-2065426295 .stroke-AA4{stroke:#EDF0FD;}
|
||||
.d2-2065426295 .stroke-AA5{stroke:#F7F8FE;}
|
||||
.d2-2065426295 .stroke-AB4{stroke:#EDF0FD;}
|
||||
.d2-2065426295 .stroke-AB5{stroke:#F7F8FE;}
|
||||
.d2-2065426295 .background-color-N1{background-color:#0A0F25;}
|
||||
.d2-2065426295 .background-color-N2{background-color:#676C7E;}
|
||||
.d2-2065426295 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-2065426295 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-2065426295 .background-color-N5{background-color:#DEE1EB;}
|
||||
.d2-2065426295 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-2065426295 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-2065426295 .background-color-B1{background-color:#0D32B2;}
|
||||
.d2-2065426295 .background-color-B2{background-color:#0D32B2;}
|
||||
.d2-2065426295 .background-color-B3{background-color:#E3E9FD;}
|
||||
.d2-2065426295 .background-color-B4{background-color:#E3E9FD;}
|
||||
.d2-2065426295 .background-color-B5{background-color:#EDF0FD;}
|
||||
.d2-2065426295 .background-color-B6{background-color:#F7F8FE;}
|
||||
.d2-2065426295 .background-color-AA2{background-color:#4A6FF3;}
|
||||
.d2-2065426295 .background-color-AA4{background-color:#EDF0FD;}
|
||||
.d2-2065426295 .background-color-AA5{background-color:#F7F8FE;}
|
||||
.d2-2065426295 .background-color-AB4{background-color:#EDF0FD;}
|
||||
.d2-2065426295 .background-color-AB5{background-color:#F7F8FE;}
|
||||
.d2-2065426295 .color-N1{color:#0A0F25;}
|
||||
.d2-2065426295 .color-N2{color:#676C7E;}
|
||||
.d2-2065426295 .color-N3{color:#9499AB;}
|
||||
.d2-2065426295 .color-N4{color:#CFD2DD;}
|
||||
.d2-2065426295 .color-N5{color:#DEE1EB;}
|
||||
.d2-2065426295 .color-N6{color:#EEF1F8;}
|
||||
.d2-2065426295 .color-N7{color:#FFFFFF;}
|
||||
.d2-2065426295 .color-B1{color:#0D32B2;}
|
||||
.d2-2065426295 .color-B2{color:#0D32B2;}
|
||||
.d2-2065426295 .color-B3{color:#E3E9FD;}
|
||||
.d2-2065426295 .color-B4{color:#E3E9FD;}
|
||||
.d2-2065426295 .color-B5{color:#EDF0FD;}
|
||||
.d2-2065426295 .color-B6{color:#F7F8FE;}
|
||||
.d2-2065426295 .color-AA2{color:#4A6FF3;}
|
||||
.d2-2065426295 .color-AA4{color:#EDF0FD;}
|
||||
.d2-2065426295 .color-AA5{color:#F7F8FE;}
|
||||
.d2-2065426295 .color-AB4{color:#EDF0FD;}
|
||||
.d2-2065426295 .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);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g id="x"><g class="shape" ><rect x="1.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g><g id="y"><g class="shape" ><rect x="0.000000" y="166.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g><g id="(x -> y)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 22.784863 67.985640 C 18.204819 106.000000 18.200000 126.000000 22.523419 162.028493" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2065426295)" /></g><g id="(y -> x)[0]"><path d="M 31.215137 164.014360 C 35.795181 126.000000 35.800000 106.000000 31.476581 69.971507" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2065426295)" /></g><mask id="d2-2065426295" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||
.d2-2561523587 .fill-N1{fill:#0A0F25;}
|
||||
.d2-2561523587 .fill-N2{fill:#676C7E;}
|
||||
.d2-2561523587 .fill-N3{fill:#9499AB;}
|
||||
.d2-2561523587 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-2561523587 .fill-N5{fill:#DEE1EB;}
|
||||
.d2-2561523587 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-2561523587 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-2561523587 .fill-B1{fill:#0D32B2;}
|
||||
.d2-2561523587 .fill-B2{fill:#0D32B2;}
|
||||
.d2-2561523587 .fill-B3{fill:#E3E9FD;}
|
||||
.d2-2561523587 .fill-B4{fill:#E3E9FD;}
|
||||
.d2-2561523587 .fill-B5{fill:#EDF0FD;}
|
||||
.d2-2561523587 .fill-B6{fill:#F7F8FE;}
|
||||
.d2-2561523587 .fill-AA2{fill:#4A6FF3;}
|
||||
.d2-2561523587 .fill-AA4{fill:#EDF0FD;}
|
||||
.d2-2561523587 .fill-AA5{fill:#F7F8FE;}
|
||||
.d2-2561523587 .fill-AB4{fill:#EDF0FD;}
|
||||
.d2-2561523587 .fill-AB5{fill:#F7F8FE;}
|
||||
.d2-2561523587 .stroke-N1{stroke:#0A0F25;}
|
||||
.d2-2561523587 .stroke-N2{stroke:#676C7E;}
|
||||
.d2-2561523587 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-2561523587 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-2561523587 .stroke-N5{stroke:#DEE1EB;}
|
||||
.d2-2561523587 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-2561523587 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-2561523587 .stroke-B1{stroke:#0D32B2;}
|
||||
.d2-2561523587 .stroke-B2{stroke:#0D32B2;}
|
||||
.d2-2561523587 .stroke-B3{stroke:#E3E9FD;}
|
||||
.d2-2561523587 .stroke-B4{stroke:#E3E9FD;}
|
||||
.d2-2561523587 .stroke-B5{stroke:#EDF0FD;}
|
||||
.d2-2561523587 .stroke-B6{stroke:#F7F8FE;}
|
||||
.d2-2561523587 .stroke-AA2{stroke:#4A6FF3;}
|
||||
.d2-2561523587 .stroke-AA4{stroke:#EDF0FD;}
|
||||
.d2-2561523587 .stroke-AA5{stroke:#F7F8FE;}
|
||||
.d2-2561523587 .stroke-AB4{stroke:#EDF0FD;}
|
||||
.d2-2561523587 .stroke-AB5{stroke:#F7F8FE;}
|
||||
.d2-2561523587 .background-color-N1{background-color:#0A0F25;}
|
||||
.d2-2561523587 .background-color-N2{background-color:#676C7E;}
|
||||
.d2-2561523587 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-2561523587 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-2561523587 .background-color-N5{background-color:#DEE1EB;}
|
||||
.d2-2561523587 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-2561523587 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-2561523587 .background-color-B1{background-color:#0D32B2;}
|
||||
.d2-2561523587 .background-color-B2{background-color:#0D32B2;}
|
||||
.d2-2561523587 .background-color-B3{background-color:#E3E9FD;}
|
||||
.d2-2561523587 .background-color-B4{background-color:#E3E9FD;}
|
||||
.d2-2561523587 .background-color-B5{background-color:#EDF0FD;}
|
||||
.d2-2561523587 .background-color-B6{background-color:#F7F8FE;}
|
||||
.d2-2561523587 .background-color-AA2{background-color:#4A6FF3;}
|
||||
.d2-2561523587 .background-color-AA4{background-color:#EDF0FD;}
|
||||
.d2-2561523587 .background-color-AA5{background-color:#F7F8FE;}
|
||||
.d2-2561523587 .background-color-AB4{background-color:#EDF0FD;}
|
||||
.d2-2561523587 .background-color-AB5{background-color:#F7F8FE;}
|
||||
.d2-2561523587 .color-N1{color:#0A0F25;}
|
||||
.d2-2561523587 .color-N2{color:#676C7E;}
|
||||
.d2-2561523587 .color-N3{color:#9499AB;}
|
||||
.d2-2561523587 .color-N4{color:#CFD2DD;}
|
||||
.d2-2561523587 .color-N5{color:#DEE1EB;}
|
||||
.d2-2561523587 .color-N6{color:#EEF1F8;}
|
||||
.d2-2561523587 .color-N7{color:#FFFFFF;}
|
||||
.d2-2561523587 .color-B1{color:#0D32B2;}
|
||||
.d2-2561523587 .color-B2{color:#0D32B2;}
|
||||
.d2-2561523587 .color-B3{color:#E3E9FD;}
|
||||
.d2-2561523587 .color-B4{color:#E3E9FD;}
|
||||
.d2-2561523587 .color-B5{color:#EDF0FD;}
|
||||
.d2-2561523587 .color-B6{color:#F7F8FE;}
|
||||
.d2-2561523587 .color-AA2{color:#4A6FF3;}
|
||||
.d2-2561523587 .color-AA4{color:#EDF0FD;}
|
||||
.d2-2561523587 .color-AA5{color:#F7F8FE;}
|
||||
.d2-2561523587 .color-AB4{color:#EDF0FD;}
|
||||
.d2-2561523587 .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);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><g id="x"><g class="shape" ><rect x="1.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g><g id="y"><g class="shape" ><rect x="0.000000" y="166.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g><g id="(x -> y)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 22.784731 67.985636 C 18.204000 106.000000 18.200001 126.000000 22.523419 162.028493" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2561523587)" /></g><g id="(y -> x)[0]"><path d="M 31.214269 164.014364 C 35.794998 126.000000 35.799999 106.000000 31.476581 69.971507" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2561523587)" /></g><mask id="d2-2561523587" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||
<rect x="-101" y="-101" width="256" height="434" fill="white"></rect>
|
||||
|
||||
</mask></svg></svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |