From d6fddfc7492636c5caf370bf2f269c3fba0e1169 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Mon, 28 Nov 2022 14:10:30 -0800 Subject: [PATCH 1/4] add transparent_3d test --- e2etests/stable_test.go | 14 ++++++ .../transparent_3d/dagre/board.exp.json | 44 +++++++++++++++++++ .../transparent_3d/dagre/sketch.exp.svg | 24 ++++++++++ .../stable/transparent_3d/elk/board.exp.json | 44 +++++++++++++++++++ .../stable/transparent_3d/elk/sketch.exp.svg | 24 ++++++++++ 5 files changed, 150 insertions(+) create mode 100644 e2etests/testdata/stable/transparent_3d/dagre/board.exp.json create mode 100644 e2etests/testdata/stable/transparent_3d/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/stable/transparent_3d/elk/board.exp.json create mode 100644 e2etests/testdata/stable/transparent_3d/elk/sketch.exp.svg diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index b319561cd..dc2e70b72 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -986,6 +986,20 @@ sugar -> c c: mixed together c -> solution: we get +`, + }, + { + name: "transparent_3d", + script: ` +cube: { + style: { + 3d: true + opacity: 0.5 + fill: orange + stroke: "#53C0D8" + stroke-width: 7 + } +} `, }, } diff --git a/e2etests/testdata/stable/transparent_3d/dagre/board.exp.json b/e2etests/testdata/stable/transparent_3d/dagre/board.exp.json new file mode 100644 index 000000000..ce7e26c0c --- /dev/null +++ b/e2etests/testdata/stable/transparent_3d/dagre/board.exp.json @@ -0,0 +1,44 @@ +{ + "name": "", + "shapes": [ + { + "id": "cube", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 139, + "height": 126, + "level": 1, + "opacity": 0.5, + "strokeDash": 0, + "strokeWidth": 7, + "borderRadius": 0, + "fill": "orange", + "stroke": "#53C0D8", + "shadow": false, + "3d": true, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "fields": null, + "methods": null, + "columns": null, + "label": "cube", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 39, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER" + } + ], + "connections": [] +} diff --git a/e2etests/testdata/stable/transparent_3d/dagre/sketch.exp.svg b/e2etests/testdata/stable/transparent_3d/dagre/sketch.exp.svg new file mode 100644 index 000000000..71fe70090 --- /dev/null +++ b/e2etests/testdata/stable/transparent_3d/dagre/sketch.exp.svg @@ -0,0 +1,24 @@ + +cube \ No newline at end of file diff --git a/e2etests/testdata/stable/transparent_3d/elk/board.exp.json b/e2etests/testdata/stable/transparent_3d/elk/board.exp.json new file mode 100644 index 000000000..af5b43a50 --- /dev/null +++ b/e2etests/testdata/stable/transparent_3d/elk/board.exp.json @@ -0,0 +1,44 @@ +{ + "name": "", + "shapes": [ + { + "id": "cube", + "type": "", + "pos": { + "x": 12, + "y": 12 + }, + "width": 139, + "height": 126, + "level": 1, + "opacity": 0.5, + "strokeDash": 0, + "strokeWidth": 7, + "borderRadius": 0, + "fill": "orange", + "stroke": "#53C0D8", + "shadow": false, + "3d": true, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "fields": null, + "methods": null, + "columns": null, + "label": "cube", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 39, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER" + } + ], + "connections": [] +} diff --git a/e2etests/testdata/stable/transparent_3d/elk/sketch.exp.svg b/e2etests/testdata/stable/transparent_3d/elk/sketch.exp.svg new file mode 100644 index 000000000..6006c9dd0 --- /dev/null +++ b/e2etests/testdata/stable/transparent_3d/elk/sketch.exp.svg @@ -0,0 +1,24 @@ + +cube \ No newline at end of file From ffab97e7788b0de704cb4bf39acb8ff268734347 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Mon, 28 Nov 2022 14:04:45 -0800 Subject: [PATCH 2/4] render 3d border all in one path to avoid (transparent) overlapping segments --- d2renderers/d2svg/d2svg.go | 132 +++++++++++++++++++++++++------------ e2etests/stable_test.go | 2 +- 2 files changed, 92 insertions(+), 42 deletions(-) diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 89ee90444..8643768b7 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -509,6 +509,91 @@ func defineShadowFilter(writer io.Writer) { `) } +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) + } + lineTo := func(p d2target.Point) string { + return fmt.Sprintf("L%d,%d ", p.X+targetShape.Pos.X, p.Y+targetShape.Pos.Y) + } + + // draw border all in one path to prevent overlapping sections + var borderSegments []string + borderSegments = append(borderSegments, + moveTo(d2target.Point{X: 0, Y: 0}), + ) + for _, v := range []d2target.Point{ + {X: threeDeeOffset, Y: -threeDeeOffset}, + {X: targetShape.Width + threeDeeOffset, Y: -threeDeeOffset}, + {X: targetShape.Width + threeDeeOffset, Y: targetShape.Height - threeDeeOffset}, + {X: targetShape.Width, Y: targetShape.Height}, + {X: 0, Y: targetShape.Height}, + {X: 0, Y: 0}, + {X: targetShape.Width, Y: 0}, + {X: targetShape.Width, Y: targetShape.Height}, + } { + borderSegments = append(borderSegments, lineTo(v)) + } + // move to top right to draw last segment without overlapping + borderSegments = append(borderSegments, + moveTo(d2target.Point{X: targetShape.Width, Y: 0}), + ) + borderSegments = append(borderSegments, + lineTo(d2target.Point{X: targetShape.Width + threeDeeOffset, Y: -threeDeeOffset}), + ) + border := targetShape + border.Fill = "none" + borderStyle := shapeStyle(border) + renderedBorder := fmt.Sprintf(``, + strings.Join(borderSegments, ""), borderStyle) + + // create mask from border stroke, to cut away from the shape fills + maskID := fmt.Sprintf("border-mask-%v", escapeText(targetShape.ID)) + borderMask := strings.Join([]string{ + fmt.Sprintf(``, + maskID, targetShape.Pos.X, targetShape.Pos.Y-threeDeeOffset, targetShape.Width+threeDeeOffset, targetShape.Height+threeDeeOffset, + ), + fmt.Sprintf(``, + targetShape.Pos.X, targetShape.Pos.Y-threeDeeOffset, targetShape.Width+threeDeeOffset, targetShape.Height+threeDeeOffset, + ), + fmt.Sprintf(``, + strings.Join(borderSegments, ""), borderStyle), + }, "\n") + + // render the main rectangle without stroke and the border mask + mainShape := targetShape + mainShape.Stroke = "none" + mainRect := fmt.Sprintf(``, + targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, shapeStyle(mainShape), maskID, + ) + + // render the side shapes in the darkened color without stroke and the border mask + var sidePoints []string + for _, v := range []d2target.Point{ + {X: 0, Y: 0}, + {X: threeDeeOffset, Y: -threeDeeOffset}, + {X: targetShape.Width + threeDeeOffset, Y: -threeDeeOffset}, + {X: targetShape.Width + threeDeeOffset, Y: targetShape.Height - threeDeeOffset}, + {X: targetShape.Width, Y: targetShape.Height}, + {X: targetShape.Width, Y: 0}, + } { + sidePoints = append(sidePoints, + fmt.Sprintf("%d,%d", v.X+targetShape.Pos.X, v.Y+targetShape.Pos.Y), + ) + } + darkerColor, err := color.Darken(targetShape.Fill) + if err != nil { + darkerColor = targetShape.Fill + } + sideShape := targetShape + sideShape.Fill = darkerColor + sideShape.Stroke = "none" + renderedSides := fmt.Sprintf(``, + strings.Join(sidePoints, " "), shapeStyle(sideShape), maskID) + + return borderMask + mainRect + renderedSides + renderedBorder +} + func drawShape(writer io.Writer, targetShape d2target.Shape) error { fmt.Fprintf(writer, ``, escapeText(targetShape.ID)) tl := geo.NewPoint(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)) @@ -561,50 +646,15 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error { // TODO should standardize "" to rectangle case d2target.ShapeRectangle, "": if targetShape.ThreeDee { - darkerColor, err := color.Darken(targetShape.Fill) - if err != nil { - darkerColor = targetShape.Fill + fmt.Fprint(writer, render3dRect(targetShape)) + } else { + if targetShape.Multiple { + fmt.Fprintf(writer, ``, + targetShape.Pos.X+10, targetShape.Pos.Y-10, targetShape.Width, targetShape.Height, style) } - sideShape := targetShape - sideShape.Fill = darkerColor - sideStyle := shapeStyle(sideShape) - - var topPolygonPoints []string - for _, v := range []d2target.Point{ - {X: 0, Y: 0}, - {X: threeDeeOffset, Y: -1 * threeDeeOffset}, - {X: targetShape.Width + threeDeeOffset, Y: -1 * threeDeeOffset}, - {X: targetShape.Width, Y: 0}, - {X: 0, Y: 0}, - } { - topPolygonPoints = append(topPolygonPoints, - fmt.Sprintf("%d,%d ", v.X+targetShape.Pos.X, v.Y+targetShape.Pos.Y), - ) - } - fmt.Fprintf(writer, ``, - strings.Join(topPolygonPoints, ""), sideStyle) - - var rightPolygonPoints []string - for _, v := range []d2target.Point{ - {X: targetShape.Width, Y: 0}, - {X: targetShape.Width + threeDeeOffset, Y: -1 * threeDeeOffset}, - {X: targetShape.Width + threeDeeOffset, Y: targetShape.Height - threeDeeOffset}, - {X: targetShape.Width, Y: targetShape.Height}, - } { - rightPolygonPoints = append(rightPolygonPoints, - fmt.Sprintf("%d,%d ", v.X+targetShape.Pos.X, v.Y+targetShape.Pos.Y), - ) - } - fmt.Fprintf(writer, ``, - strings.Join(rightPolygonPoints, ""), sideStyle) - } - if targetShape.Multiple { fmt.Fprintf(writer, ``, - targetShape.Pos.X+10, targetShape.Pos.Y-10, targetShape.Width, targetShape.Height, style) + targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) } - fmt.Fprintf(writer, ``, - targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) - case d2target.ShapeText, d2target.ShapeCode: default: if targetShape.Multiple { diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index dc2e70b72..f3c89d37c 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -913,7 +913,7 @@ y: { opacity: 0.6 fill: red 3d: true - stroke: black + stroke: black } } From ec1d3e47b46ad7ab5d50d3f57d5c0d7ba962708d Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Mon, 28 Nov 2022 14:26:57 -0800 Subject: [PATCH 3/4] update tests --- e2etests/testdata/stable/square_3d/dagre/sketch.exp.svg | 6 +++++- e2etests/testdata/stable/square_3d/elk/sketch.exp.svg | 6 +++++- e2etests/testdata/stable/stylish/dagre/sketch.exp.svg | 4 +++- e2etests/testdata/stable/stylish/elk/sketch.exp.svg | 4 +++- .../testdata/stable/transparent_3d/dagre/sketch.exp.svg | 4 +++- e2etests/testdata/stable/transparent_3d/elk/sketch.exp.svg | 4 +++- 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/e2etests/testdata/stable/square_3d/dagre/sketch.exp.svg b/e2etests/testdata/stable/square_3d/dagre/sketch.exp.svg index 8756afe80..550462a61 100644 --- a/e2etests/testdata/stable/square_3d/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/square_3d/dagre/sketch.exp.svg @@ -14,7 +14,11 @@ width="371" height="580" viewBox="-100 -100 371 580">rectanglesquare + +rectangle + +square rectanglesquare + +rectangle + +square cube + +cubecube + +cube -rectangle +rectangle -square -rectangle +rectangle -square -cube -cube