fixed requested changes
This commit is contained in:
parent
c3481f1187
commit
c2347859a2
7 changed files with 458 additions and 212 deletions
|
|
@ -495,58 +495,49 @@ func makeLabelMask(labelTL *geo.Point, width, height int, opacity float64) strin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given control points p1, p2, p3, p4, calculate the segment of this bezier curve from t0 -> t1 where {0 <= t0 < t1 <= 1}
|
// Gets a certain line/curve's SVG path string. offsetIdx and pathData provides the points needed
|
||||||
func bezierCurveSegment(p1, p2, p3, p4 *geo.Point, t0, t1 float64) (geo.Point, geo.Point, geo.Point, geo.Point) {
|
func getSVGPathString(path *string, pathType *string, offsetIdx int, pathData []string) (string, error) {
|
||||||
u0, u1 := 1-t0, 1-t1
|
var returnPath string
|
||||||
|
|
||||||
q1 := geo.Point{
|
switch *pathType {
|
||||||
X: (u0*u0*u0)*p1.X + (3*t0*u0*u0)*p2.X + (3*t0*t0*u0)*p3.X + t0*t0*t0*p4.X,
|
case "M":
|
||||||
Y: (u0*u0*u0)*p1.Y + (3*t0*u0*u0)*p2.Y + (3*t0*t0*u0)*p3.Y + t0*t0*t0*p4.Y,
|
returnPath = fmt.Sprintf("M %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2])
|
||||||
}
|
case "L":
|
||||||
q2 := geo.Point{
|
returnPath = fmt.Sprintf("L %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2])
|
||||||
X: (u0*u0*u1)*p1.X + (2*t0*u0*u1+u0*u0*t1)*p2.X + (t0*t0*u1+2*u0*t0*t1)*p3.X + t0*t0*t1*p4.X,
|
case "C":
|
||||||
Y: (u0*u0*u1)*p1.Y + (2*t0*u0*u1+u0*u0*t1)*p2.Y + (t0*t0*u1+2*u0*t0*t1)*p3.Y + t0*t0*t1*p4.Y,
|
returnPath = fmt.Sprintf("C %s %s %s %s %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2], pathData[offsetIdx+3], pathData[offsetIdx+4], pathData[offsetIdx+5], pathData[offsetIdx+6])
|
||||||
}
|
case "S":
|
||||||
q3 := geo.Point{
|
returnPath = fmt.Sprintf("S %s %s %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2], pathData[offsetIdx+3], pathData[offsetIdx+4])
|
||||||
X: (u0*u1*u1)*p1.X + (t0*u1*u1+2*u0*t1*u1)*p2.X + (2*t0*t1*u1+u0*t1*t1)*p3.X + t0*t1*t1*p4.X,
|
default:
|
||||||
Y: (u0*u1*u1)*p1.Y + (t0*u1*u1+2*u0*t1*u1)*p2.Y + (2*t0*t1*u1+u0*t1*t1)*p3.Y + t0*t1*t1*p4.Y,
|
return "", fmt.Errorf("unknown svg path command \"%s\"", pathData[offsetIdx])
|
||||||
}
|
|
||||||
q4 := geo.Point{
|
|
||||||
X: (u1*u1*u1)*p1.X + (3*t1*u1*u1)*p2.X + (3*t1*t1*u1)*p3.X + t1*t1*t1*p4.X,
|
|
||||||
Y: (u1*u1*u1)*p1.Y + (3*t1*u1*u1)*p2.Y + (3*t1*t1*u1)*p3.Y + t1*t1*t1*p4.Y,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return q1, q2, q3, q4
|
return returnPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a certain line/curve to an existing SVG path string. offsetIdx and pathData provides the points needed
|
// Gets how much to increment by on an SVG string to get to the next path command
|
||||||
func addToPath(path *string, pathType *string, offsetIdx int, pathData []string) int {
|
func getPathStringIncrement(pathType *string) (int, error) {
|
||||||
var increment int
|
var increment int
|
||||||
|
|
||||||
switch *pathType {
|
switch *pathType {
|
||||||
case "M":
|
case "M":
|
||||||
*path += fmt.Sprintf("M %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2])
|
|
||||||
increment = 3
|
increment = 3
|
||||||
case "L":
|
case "L":
|
||||||
*path += fmt.Sprintf("L %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2])
|
|
||||||
increment = 3
|
increment = 3
|
||||||
case "C":
|
case "C":
|
||||||
*path += fmt.Sprintf("C %s %s %s %s %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2], pathData[offsetIdx+3], pathData[offsetIdx+4], pathData[offsetIdx+5], pathData[offsetIdx+6])
|
|
||||||
increment = 7
|
increment = 7
|
||||||
case "S":
|
case "S":
|
||||||
*path += fmt.Sprintf("S %s %s %s %s ", pathData[offsetIdx+1], pathData[offsetIdx+2], pathData[offsetIdx+3], pathData[offsetIdx+4])
|
|
||||||
increment = 5
|
increment = 5
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown svg path command \"%s\"", pathData[offsetIdx]))
|
return 0, fmt.Errorf("unknown svg path command \"%s\"", *pathType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return increment
|
return increment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function finds the length of a path in SVG notation
|
// This function finds the length of a path in SVG notation
|
||||||
func pathLength(pathData []string) float64 {
|
func pathLength(pathData []string) (float64, error) {
|
||||||
var x, y, pathLength float64
|
var x, y, pathLength float64
|
||||||
var increment int
|
|
||||||
var prevPosition geo.Point
|
var prevPosition geo.Point
|
||||||
|
|
||||||
for i := 0; i < len(pathData); {
|
for i := 0; i < len(pathData); {
|
||||||
|
|
@ -554,50 +545,51 @@ func pathLength(pathData []string) float64 {
|
||||||
case "M":
|
case "M":
|
||||||
x, _ = strconv.ParseFloat(pathData[i+1], 64)
|
x, _ = strconv.ParseFloat(pathData[i+1], 64)
|
||||||
y, _ = strconv.ParseFloat(pathData[i+2], 64)
|
y, _ = strconv.ParseFloat(pathData[i+2], 64)
|
||||||
|
|
||||||
increment = 3
|
|
||||||
case "L":
|
case "L":
|
||||||
x, _ = strconv.ParseFloat(pathData[i+1], 64)
|
x, _ = strconv.ParseFloat(pathData[i+1], 64)
|
||||||
y, _ = strconv.ParseFloat(pathData[i+2], 64)
|
y, _ = strconv.ParseFloat(pathData[i+2], 64)
|
||||||
|
|
||||||
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
||||||
|
|
||||||
increment = 3
|
|
||||||
case "C":
|
case "C":
|
||||||
x, _ = strconv.ParseFloat(pathData[i+5], 64)
|
x, _ = strconv.ParseFloat(pathData[i+5], 64)
|
||||||
y, _ = strconv.ParseFloat(pathData[i+6], 64)
|
y, _ = strconv.ParseFloat(pathData[i+6], 64)
|
||||||
|
|
||||||
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
||||||
|
|
||||||
increment = 7
|
|
||||||
case "S":
|
case "S":
|
||||||
x, _ = strconv.ParseFloat(pathData[i+3], 64)
|
x, _ = strconv.ParseFloat(pathData[i+3], 64)
|
||||||
y, _ = strconv.ParseFloat(pathData[i+4], 64)
|
y, _ = strconv.ParseFloat(pathData[i+4], 64)
|
||||||
|
|
||||||
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
pathLength += geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
||||||
|
|
||||||
increment = 5
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown svg path command \"%s\"", pathData[i]))
|
return 0, fmt.Errorf("unknown svg path command \"%s\"", pathData[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPosition = geo.Point{X: x, Y: y}
|
prevPosition = geo.Point{X: x, Y: y}
|
||||||
|
increment, err := getPathStringIncrement(&pathData[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
i += increment
|
i += increment
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathLength
|
return pathLength, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splits a SVG path into two SVG paths, with the first path being ~{percentage}% of the path
|
// Splits a SVG path into two SVG paths, with the first path being ~{percentage}% of the path
|
||||||
func splitPath(path string, percentage float64) (string, string) {
|
func splitPath(path string, percentage float64) (string, string, error) {
|
||||||
var sumPathLens, curPathLen, x, y float64
|
var sumPathLens, curPathLen, x, y float64
|
||||||
var prevPosition geo.Point
|
var prevPosition geo.Point
|
||||||
var path1, path2 string
|
var path1, path2 string
|
||||||
var increment int
|
|
||||||
|
|
||||||
pastHalf := false
|
pastHalf := false
|
||||||
pathData := strings.Split(path, " ")
|
pathData := strings.Split(path, " ")
|
||||||
pathLen := pathLength(pathData)
|
pathLen, err := pathLength(pathData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
|
|
@ -623,13 +615,24 @@ func splitPath(path string, percentage float64) (string, string) {
|
||||||
|
|
||||||
curPathLen = geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
curPathLen = geo.EuclideanDistance(prevPosition.X, prevPosition.Y, x, y)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown svg path command \"%s\"", pathData[i]))
|
return "", "", fmt.Errorf("unknown svg path command \"%s\"", pathData[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
if pastHalf {
|
if pastHalf {
|
||||||
increment = addToPath(&path2, &pathData[i], i, pathData)
|
curPath, err := getSVGPathString(&path2, &pathData[i], i, pathData)
|
||||||
|
path2 += curPath
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
} else if sumPathLens+curPathLen < pathLen*percentage {
|
} else if sumPathLens+curPathLen < pathLen*percentage {
|
||||||
increment = addToPath(&path1, &pathData[i], i, pathData)
|
curPath, err := getSVGPathString(&path2, &pathData[i], i, pathData)
|
||||||
|
path1 += curPath
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sumPathLens += curPathLen
|
sumPathLens += curPathLen
|
||||||
|
|
@ -641,8 +644,6 @@ func splitPath(path string, percentage float64) (string, string) {
|
||||||
case "L":
|
case "L":
|
||||||
path1 += fmt.Sprintf("L %f %f ", (x-prevPosition.X)*t+prevPosition.X, (y-prevPosition.Y)*t+prevPosition.Y)
|
path1 += fmt.Sprintf("L %f %f ", (x-prevPosition.X)*t+prevPosition.X, (y-prevPosition.Y)*t+prevPosition.Y)
|
||||||
path2 += fmt.Sprintf("M %f %f L %f %f ", (x-prevPosition.X)*t+prevPosition.X, (y-prevPosition.Y)*t+prevPosition.Y, x, y)
|
path2 += fmt.Sprintf("M %f %f L %f %f ", (x-prevPosition.X)*t+prevPosition.X, (y-prevPosition.Y)*t+prevPosition.Y, x, y)
|
||||||
|
|
||||||
increment = 3
|
|
||||||
case "C":
|
case "C":
|
||||||
h1x, _ := strconv.ParseFloat(pathData[i+1], 64)
|
h1x, _ := strconv.ParseFloat(pathData[i+1], 64)
|
||||||
h1y, _ := strconv.ParseFloat(pathData[i+2], 64)
|
h1y, _ := strconv.ParseFloat(pathData[i+2], 64)
|
||||||
|
|
@ -653,31 +654,33 @@ func splitPath(path string, percentage float64) (string, string) {
|
||||||
heading2 := geo.Point{X: h2x, Y: h2y}
|
heading2 := geo.Point{X: h2x, Y: h2y}
|
||||||
nextPoint := geo.Point{X: x, Y: y}
|
nextPoint := geo.Point{X: x, Y: y}
|
||||||
|
|
||||||
q1, q2, q3, q4 := bezierCurveSegment(&prevPosition, &heading1, &heading2, &nextPoint, 0, 0.5)
|
q1, q2, q3, q4 := svg.BezierCurveSegment(&prevPosition, &heading1, &heading2, &nextPoint, 0, 0.5)
|
||||||
path1 += fmt.Sprintf("C %f %f %f %f %f %f ", q2.X, q2.Y, q3.X, q3.Y, q4.X, q4.Y)
|
path1 += fmt.Sprintf("C %f %f %f %f %f %f ", q2.X, q2.Y, q3.X, q3.Y, q4.X, q4.Y)
|
||||||
|
|
||||||
q1, q2, q3, q4 = bezierCurveSegment(&prevPosition, &heading1, &heading2, &nextPoint, 0.5, 1)
|
q1, q2, q3, q4 = svg.BezierCurveSegment(&prevPosition, &heading1, &heading2, &nextPoint, 0.5, 1)
|
||||||
path2 += fmt.Sprintf("M %f %f C %f %f %f %f %f %f ", q1.X, q1.Y, q2.X, q2.Y, q3.X, q3.Y, q4.X, q4.Y)
|
path2 += fmt.Sprintf("M %f %f C %f %f %f %f %f %f ", q1.X, q1.Y, q2.X, q2.Y, q3.X, q3.Y, q4.X, q4.Y)
|
||||||
|
|
||||||
increment = 7
|
|
||||||
case "S":
|
case "S":
|
||||||
// Skip S curves because they are shorter and we can split along the connection to the next path instead
|
// Skip S curves because they are shorter and we can split along the connection to the next path instead
|
||||||
path1 += fmt.Sprintf("S %s %s %s %s ", pathData[i+1], pathData[i+2], pathData[i+3], pathData[i+4])
|
path1 += fmt.Sprintf("S %s %s %s %s ", pathData[i+1], pathData[i+2], pathData[i+3], pathData[i+4])
|
||||||
path2 += fmt.Sprintf("M %s %s ", pathData[i+3], pathData[i+4])
|
path2 += fmt.Sprintf("M %s %s ", pathData[i+3], pathData[i+4])
|
||||||
|
|
||||||
increment = 5
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown svg path command \"%s\"", pathData[i]))
|
return "", "", fmt.Errorf("unknown svg path command \"%s\"", pathData[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
pastHalf = true
|
pastHalf = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
increment, err := getPathStringIncrement(&pathData[i])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
i += increment
|
i += increment
|
||||||
prevPosition = geo.Point{X: x, Y: y}
|
prevPosition = geo.Point{X: x, Y: y}
|
||||||
}
|
}
|
||||||
|
|
||||||
return path1, path2
|
return path1, path2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Connection, markers map[string]struct{}, idToShape map[string]d2target.Shape, sketchRunner *d2sketch.Runner) (labelMask string, _ error) {
|
func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Connection, markers map[string]struct{}, idToShape map[string]d2target.Shape, sketchRunner *d2sketch.Runner) (labelMask string, _ error) {
|
||||||
|
|
@ -766,7 +769,11 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
|
||||||
pathEl.Attributes = fmt.Sprintf("%s%s%s", markerStart, markerEnd, mask)
|
pathEl.Attributes = fmt.Sprintf("%s%s%s", markerStart, markerEnd, mask)
|
||||||
fmt.Fprint(writer, pathEl.Render())
|
fmt.Fprint(writer, pathEl.Render())
|
||||||
} else {
|
} else {
|
||||||
path1, path2 := splitPath(path, 0.5)
|
path1, path2, err := splitPath(path, 0.5)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
pathEl1 := d2themes.NewThemableElement("path")
|
pathEl1 := d2themes.NewThemableElement("path")
|
||||||
pathEl1.D = path1
|
pathEl1.D = path1
|
||||||
|
|
|
||||||
1
e2etests/testdata/txtar.txt
vendored
1
e2etests/testdata/txtar.txt
vendored
|
|
@ -220,6 +220,7 @@ a <-> c: {style.animated: true}
|
||||||
a <-> d: {style.animated: true}
|
a <-> d: {style.animated: true}
|
||||||
a <-> e: {style.animated: true}
|
a <-> e: {style.animated: true}
|
||||||
f <-> g: {style.animated: true}
|
f <-> g: {style.animated: true}
|
||||||
|
x -- x: {style.animated: true}
|
||||||
|
|
||||||
-- opacity-zero-route --
|
-- opacity-zero-route --
|
||||||
grid: {
|
grid: {
|
||||||
|
|
|
||||||
124
e2etests/testdata/txtar/bidirectional_connection_animation/dagre/board.exp.json
generated
vendored
124
e2etests/testdata/txtar/bidirectional_connection_animation/dagre/board.exp.json
generated
vendored
|
|
@ -289,6 +289,47 @@
|
||||||
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
||||||
"zIndex": 0,
|
"zIndex": 0,
|
||||||
"level": 1
|
"level": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "x",
|
||||||
|
"type": "rectangle",
|
||||||
|
"pos": {
|
||||||
|
"x": 566,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"width": 53,
|
||||||
|
"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": "x",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": "DEFAULT",
|
||||||
|
"language": "",
|
||||||
|
"color": "N1",
|
||||||
|
"italic": false,
|
||||||
|
"bold": true,
|
||||||
|
"underline": false,
|
||||||
|
"labelWidth": 8,
|
||||||
|
"labelHeight": 21,
|
||||||
|
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
||||||
|
"zIndex": 0,
|
||||||
|
"level": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connections": [
|
"connections": [
|
||||||
|
|
@ -526,6 +567,89 @@
|
||||||
"tooltip": "",
|
"tooltip": "",
|
||||||
"icon": null,
|
"icon": null,
|
||||||
"zIndex": 0
|
"zIndex": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "(x -- x)[0]",
|
||||||
|
"src": "x",
|
||||||
|
"srcArrow": "none",
|
||||||
|
"dst": "x",
|
||||||
|
"dstArrow": "none",
|
||||||
|
"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": 619.166015625,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 639.9660034179688,
|
||||||
|
"y": 3.1989998817443848
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 646.5,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 648.5,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 650.5,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 653.166015625,
|
||||||
|
"y": 6.599999904632568
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 655.166015625,
|
||||||
|
"y": 16.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 657.166015625,
|
||||||
|
"y": 26.399999618530273
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 657.166015625,
|
||||||
|
"y": 39.599998474121094
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 655.166015625,
|
||||||
|
"y": 49.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 653.166015625,
|
||||||
|
"y": 59.400001525878906
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 639.9660034179688,
|
||||||
|
"y": 62.79999923706055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 619.166015625,
|
||||||
|
"y": 50
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isCurve": true,
|
||||||
|
"animated": true,
|
||||||
|
"tooltip": "",
|
||||||
|
"icon": null,
|
||||||
|
"zIndex": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"root": {
|
"root": {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
87
e2etests/testdata/txtar/bidirectional_connection_animation/elk/board.exp.json
generated
vendored
87
e2etests/testdata/txtar/bidirectional_connection_animation/elk/board.exp.json
generated
vendored
|
|
@ -289,6 +289,47 @@
|
||||||
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
||||||
"zIndex": 0,
|
"zIndex": 0,
|
||||||
"level": 1
|
"level": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "x",
|
||||||
|
"type": "rectangle",
|
||||||
|
"pos": {
|
||||||
|
"x": 427,
|
||||||
|
"y": 12
|
||||||
|
},
|
||||||
|
"width": 53,
|
||||||
|
"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": "x",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": "DEFAULT",
|
||||||
|
"language": "",
|
||||||
|
"color": "N1",
|
||||||
|
"italic": false,
|
||||||
|
"bold": true,
|
||||||
|
"underline": false,
|
||||||
|
"labelWidth": 8,
|
||||||
|
"labelHeight": 21,
|
||||||
|
"labelPosition": "INSIDE_MIDDLE_CENTER",
|
||||||
|
"zIndex": 0,
|
||||||
|
"level": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connections": [
|
"connections": [
|
||||||
|
|
@ -513,6 +554,52 @@
|
||||||
"tooltip": "",
|
"tooltip": "",
|
||||||
"icon": null,
|
"icon": null,
|
||||||
"zIndex": 0
|
"zIndex": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "(x -- x)[0]",
|
||||||
|
"src": "x",
|
||||||
|
"srcArrow": "none",
|
||||||
|
"dst": "x",
|
||||||
|
"dstArrow": "none",
|
||||||
|
"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": 427.5,
|
||||||
|
"y": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 377.5,
|
||||||
|
"y": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 377.5,
|
||||||
|
"y": 56
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 427.5,
|
||||||
|
"y": 56
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"animated": true,
|
||||||
|
"tooltip": "",
|
||||||
|
"icon": null,
|
||||||
|
"zIndex": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"root": {
|
"root": {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
|
|
@ -114,3 +114,28 @@ func GetStrokeDashAttributes(strokeWidth, dashGapSize float64) (float64, float64
|
||||||
scaledGapSize := scale * scaledDashSize
|
scaledGapSize := scale * scaledDashSize
|
||||||
return scaledDashSize, scaledGapSize
|
return scaledDashSize, scaledGapSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given control points p1, p2, p3, p4, calculate the segment of this bezier curve from t0 -> t1 where {0 <= t0 < t1 <= 1}.
|
||||||
|
// Uses De Casteljau's algorithm, referenced: https://stackoverflow.com/questions/11703283/cubic-bezier-curve-segment/11704152#11704152
|
||||||
|
func BezierCurveSegment(p1, p2, p3, p4 *geo.Point, t0, t1 float64) (geo.Point, geo.Point, geo.Point, geo.Point) {
|
||||||
|
u0, u1 := 1-t0, 1-t1
|
||||||
|
|
||||||
|
q1 := geo.Point{
|
||||||
|
X: (u0*u0*u0)*p1.X + (3*t0*u0*u0)*p2.X + (3*t0*t0*u0)*p3.X + t0*t0*t0*p4.X,
|
||||||
|
Y: (u0*u0*u0)*p1.Y + (3*t0*u0*u0)*p2.Y + (3*t0*t0*u0)*p3.Y + t0*t0*t0*p4.Y,
|
||||||
|
}
|
||||||
|
q2 := geo.Point{
|
||||||
|
X: (u0*u0*u1)*p1.X + (2*t0*u0*u1+u0*u0*t1)*p2.X + (t0*t0*u1+2*u0*t0*t1)*p3.X + t0*t0*t1*p4.X,
|
||||||
|
Y: (u0*u0*u1)*p1.Y + (2*t0*u0*u1+u0*u0*t1)*p2.Y + (t0*t0*u1+2*u0*t0*t1)*p3.Y + t0*t0*t1*p4.Y,
|
||||||
|
}
|
||||||
|
q3 := geo.Point{
|
||||||
|
X: (u0*u1*u1)*p1.X + (t0*u1*u1+2*u0*t1*u1)*p2.X + (2*t0*t1*u1+u0*t1*t1)*p3.X + t0*t1*t1*p4.X,
|
||||||
|
Y: (u0*u1*u1)*p1.Y + (t0*u1*u1+2*u0*t1*u1)*p2.Y + (2*t0*t1*u1+u0*t1*t1)*p3.Y + t0*t1*t1*p4.Y,
|
||||||
|
}
|
||||||
|
q4 := geo.Point{
|
||||||
|
X: (u1*u1*u1)*p1.X + (3*t1*u1*u1)*p2.X + (3*t1*t1*u1)*p3.X + t1*t1*t1*p4.X,
|
||||||
|
Y: (u1*u1*u1)*p1.Y + (3*t1*u1*u1)*p2.Y + (3*t1*t1*u1)*p3.Y + t1*t1*t1*p4.Y,
|
||||||
|
}
|
||||||
|
|
||||||
|
return q1, q2, q3, q4
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue