implement s-shape

This commit is contained in:
Alexander Wang 2023-03-14 14:11:51 -07:00
parent e4b82c1708
commit 73c35bb14b
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
8 changed files with 2457 additions and 2 deletions

View file

@ -96,6 +96,7 @@ var DefaultOpts = ConfigurableOpts{
} }
var port_spacing = 40. var port_spacing = 40.
var edge_node_spacing = 40
type elkOpts struct { type elkOpts struct {
EdgeNode int `json:"elk.spacing.edgeNode,omitempty"` EdgeNode int `json:"elk.spacing.edgeNode,omitempty"`
@ -143,7 +144,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
LayoutOptions: &elkOpts{ LayoutOptions: &elkOpts{
Thoroughness: 8, Thoroughness: 8,
EdgeEdgeBetweenLayersSpacing: 50, EdgeEdgeBetweenLayersSpacing: 50,
EdgeNode: 40, EdgeNode: edge_node_spacing,
HierarchyHandling: "INCLUDE_CHILDREN", HierarchyHandling: "INCLUDE_CHILDREN",
FixedAlignment: "BALANCED", FixedAlignment: "BALANCED",
ConsiderModelOrder: "NODES_AND_EDGES", ConsiderModelOrder: "NODES_AND_EDGES",
@ -224,7 +225,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
EdgeEdgeBetweenLayersSpacing: 50, EdgeEdgeBetweenLayersSpacing: 50,
HierarchyHandling: "INCLUDE_CHILDREN", HierarchyHandling: "INCLUDE_CHILDREN",
FixedAlignment: "BALANCED", FixedAlignment: "BALANCED",
EdgeNode: 40, EdgeNode: edge_node_spacing,
ConsiderModelOrder: "NODES_AND_EDGES", ConsiderModelOrder: "NODES_AND_EDGES",
// Why is it (height, width)? I have no clue, but it works. // Why is it (height, width)? I have no clue, but it works.
NodeSizeMinimum: fmt.Sprintf("(%d, %d)", int(math.Ceil(height)), int(math.Ceil(width))), NodeSizeMinimum: fmt.Sprintf("(%d, %d)", int(math.Ceil(height)), int(math.Ceil(width))),
@ -442,5 +443,131 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
edge.Route = points edge.Route = points
} }
deleteBends(g)
return nil return nil
} }
// deleteBends is a shim for ELK to delete unnecessary bends
// see https://github.com/terrastruct/d2/issues/1030
func deleteBends(g *d2graph.Graph) {
// Get rid of S-shapes at the source and the target
for _, isSource := range []bool{true, false} {
for ei, e := range g.Edges {
if len(e.Route) < 4 {
continue
}
var endpoint *d2graph.Object
var start *geo.Point
var corner *geo.Point
var end *geo.Point
if isSource {
start = e.Route[0]
corner = e.Route[1]
end = e.Route[2]
endpoint = e.Src
} else {
start = e.Route[len(e.Route)-1]
corner = e.Route[len(e.Route)-2]
end = e.Route[len(e.Route)-3]
endpoint = e.Dst
}
isHorizontal := start.Y == corner.Y
// Make sure it's still attached
if isHorizontal {
if end.Y <= endpoint.TopLeft.Y+10 {
continue
}
if end.Y >= endpoint.TopLeft.Y+endpoint.Height-10 {
continue
}
} else {
if end.X <= endpoint.TopLeft.X+10 {
continue
}
if end.X >= endpoint.TopLeft.X+endpoint.Width-10 {
continue
}
}
var newStart *geo.Point
if isHorizontal {
newStart = geo.NewPoint(start.X, end.Y)
} else {
newStart = geo.NewPoint(end.X, start.Y)
}
// Check that the new segment doesn't collide with anything new
oldSegment := geo.NewSegment(start, corner)
newSegment := geo.NewSegment(newStart, end)
oldIntersects := countObjectIntersects(g, *oldSegment)
newIntersects := countObjectIntersects(g, *newSegment)
if newIntersects > oldIntersects {
continue
}
oldIntersects = countEdgeIntersects(g, g.Edges[ei], *oldSegment)
newIntersects = countEdgeIntersects(g, g.Edges[ei], *newSegment)
if newIntersects > oldIntersects {
continue
}
// commit
if isSource {
g.Edges[ei].Route = append(
[]*geo.Point{newStart},
e.Route[3:]...,
)
} else {
g.Edges[ei].Route = append(
e.Route[:len(e.Route)-3],
newStart,
)
}
}
}
}
func countObjectIntersects(g *d2graph.Graph, s geo.Segment) int {
count := 0
for _, o := range g.Objects {
if o.Intersects(s, float64(edge_node_spacing)) {
count++
}
}
return count
}
// countEdgeIntersects counts both crossings AND getting too close to a parallel segment
func countEdgeIntersects(g *d2graph.Graph, sEdge *d2graph.Edge, s geo.Segment) int {
isHorizontal := s.Start.Y == s.End.Y
count := 0
for _, e := range g.Edges {
if e == sEdge {
continue
}
for i := 0; i < len(e.Route)-1; i++ {
otherS := geo.NewSegment(e.Route[i], e.Route[i+1])
otherIsHorizontal := otherS.Start.Y == otherS.End.Y
if isHorizontal == otherIsHorizontal {
if s.Overlaps(*otherS, isHorizontal, float64(edge_node_spacing)) {
count++
}
} else {
if s.Intersects(*otherS) {
count++
}
}
}
}
return count
}

View file

@ -81,6 +81,59 @@ func testStable(t *testing.T) {
} }
`, `,
}, },
{
name: "elk_shim",
script: `network: {
cell tower: {
satellites: {
shape: stored_data
style.multiple: true
width: 140
}
transmitter: {
width: 140
}
satellites -> transmitter: send {
}
satellites -> transmitter: send {
}
satellites -> transmitter: send {
}
}
online portal: {
ui: { shape: hexagon }
}
data processor: {
storage: {
shape: cylinder
style.multiple: true
}
}
cell tower.transmitter -> data processor.storage: phone logs
}
user: {
shape: person
width: 130
}
user -> network.cell tower: make call
user -> network.online portal.ui: access {
style.stroke-dash: 3
}
api server -> network.online portal.ui: display
api server -> logs: persist
logs: { shape: page; style.multiple: true }
network.data processor -> api server
`,
},
{ {
name: "mono-font", name: "mono-font",
script: `satellites: SATELLITES { script: `satellites: SATELLITES {

1109
e2etests/testdata/stable/elk_shim/dagre/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 806 KiB

908
e2etests/testdata/stable/elk_shim/elk/board.exp.json generated vendored Normal file
View file

@ -0,0 +1,908 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "network",
"type": "rectangle",
"pos": {
"x": 12,
"y": 311
},
"width": 540,
"height": 892,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "network",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 96,
"labelHeight": 36,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "network.cell tower",
"type": "rectangle",
"pos": {
"x": 62,
"y": 361
},
"width": 240,
"height": 403,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "cell tower",
"fontSize": 24,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 99,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.cell tower.satellites",
"type": "stored_data",
"pos": {
"x": 112,
"y": 411
},
"width": 140,
"height": 61,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AA5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "satellites",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 65,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.cell tower.transmitter",
"type": "rectangle",
"pos": {
"x": 112,
"y": 653
},
"width": 140,
"height": 61,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "transmitter",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 83,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.online portal",
"type": "rectangle",
"pos": {
"x": 322,
"y": 366
},
"width": 180,
"height": 169,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "online portal",
"fontSize": 24,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 128,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.online portal.ui",
"type": "hexagon",
"pos": {
"x": 372,
"y": 416
},
"width": 80,
"height": 69,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "N5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "ui",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 14,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.data processor",
"type": "rectangle",
"pos": {
"x": 82,
"y": 935
},
"width": 199,
"height": 218,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "data processor",
"fontSize": 24,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 150,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.data processor.storage",
"type": "cylinder",
"pos": {
"x": 132,
"y": 985
},
"width": 99,
"height": 118,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AA5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "storage",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 54,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "user",
"type": "person",
"pos": {
"x": 286,
"y": 12
},
"width": 130,
"height": 87,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B3",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "user",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 32,
"labelHeight": 21,
"labelPosition": "OUTSIDE_BOTTOM_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "api server",
"type": "rectangle",
"pos": {
"x": 534,
"y": 59
},
"width": 116,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "api server",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 71,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "logs",
"type": "page",
"pos": {
"x": 632,
"y": 311
},
"width": 73,
"height": 87,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AB4",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "logs",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 28,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "network.cell tower.(satellites -> transmitter)[0]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "send",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 33,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 138,
"y": 472
},
{
"x": 138,
"y": 653
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.cell tower.(satellites -> transmitter)[1]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "send",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 33,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 182,
"y": 472
},
{
"x": 182,
"y": 653
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.cell tower.(satellites -> transmitter)[2]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "send",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 33,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 226,
"y": 472
},
{
"x": 226,
"y": 653
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.(cell tower.transmitter -> data processor.storage)[0]",
"src": "network.cell tower.transmitter",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.data processor.storage",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "phone logs",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 74,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 182,
"y": 714
},
{
"x": 182,
"y": 985
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(user -> network.cell tower)[0]",
"src": "user",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "make call",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 64,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 329,
"y": 99
},
{
"x": 329.33333333333337,
"y": 165
},
{
"x": 222.83333333333334,
"y": 165
},
{
"x": 222.83333333333334,
"y": 361
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(user -> network.online portal.ui)[0]",
"src": "user",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.online portal.ui",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 3,
"strokeWidth": 2,
"stroke": "B2",
"borderRadius": 10,
"label": "access",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 373,
"y": 99
},
{
"x": 372.6666666666667,
"y": 165
},
{
"x": 398.6666666666667,
"y": 165
},
{
"x": 399,
"y": 416
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(api server -> network.online portal.ui)[0]",
"src": "api server",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.online portal.ui",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "display",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 48,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 563,
"y": 125
},
{
"x": 563,
"y": 165
},
{
"x": 462.5,
"y": 165
},
{
"x": 462.5,
"y": 266
},
{
"x": 425.33333333333337,
"y": 266
},
{
"x": 425,
"y": 416
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(api server -> logs)[0]",
"src": "api server",
"srcArrow": "none",
"srcLabel": "",
"dst": "logs",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "persist",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 46,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 621,
"y": 125
},
{
"x": 621,
"y": 165
},
{
"x": 668.5,
"y": 165
},
{
"x": 669,
"y": 311
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(network.data processor -> api server)[0]",
"src": "network.data processor",
"srcArrow": "none",
"srcLabel": "",
"dst": "api server",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 132.5,
"y": 1153
},
{
"x": 132.5,
"y": 1248
},
{
"x": 592,
"y": 1248
},
{
"x": 592,
"y": 125
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 806 KiB

View file

@ -27,6 +27,28 @@ func (b *Box) Center() *Point {
return NewPoint(b.TopLeft.X+b.Width/2, b.TopLeft.Y+b.Height/2) return NewPoint(b.TopLeft.X+b.Width/2, b.TopLeft.Y+b.Height/2)
} }
// Intersects returns true if the segment comes within buffer of the box
func (b *Box) Intersects(s Segment, buffer float64) bool {
tl := NewPoint(b.TopLeft.X-buffer, b.TopLeft.Y-buffer)
tr := NewPoint(tl.X+b.Width+buffer*2, tl.Y)
br := NewPoint(tr.X, tr.Y+b.Height+buffer*2)
bl := NewPoint(tl.X, br.Y)
if p := IntersectionPoint(s.Start, s.End, tl, tr); p != nil {
return true
}
if p := IntersectionPoint(s.Start, s.End, tr, br); p != nil {
return true
}
if p := IntersectionPoint(s.Start, s.End, br, bl); p != nil {
return true
}
if p := IntersectionPoint(s.Start, s.End, bl, tl); p != nil {
return true
}
return false
}
func (b *Box) Intersections(s Segment) []*Point { func (b *Box) Intersections(s Segment) []*Point {
pts := []*Point{} pts := []*Point{}

View file

@ -38,6 +38,10 @@ func (s Segment) Overlaps(otherS Segment, isHorizontal bool, buffer float64) boo
} }
} }
func (segment Segment) Intersects(otherSegment Segment) bool {
return IntersectionPoint(segment.Start, segment.End, otherSegment.Start, otherSegment.End) != nil
}
//nolint:unused //nolint:unused
func (s Segment) ToString() string { func (s Segment) ToString() string {
return fmt.Sprintf("%v -> %v", s.Start.ToString(), s.End.ToString()) return fmt.Sprintf("%v -> %v", s.Start.ToString(), s.End.ToString())