diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 5801712b8..255f8ec64 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -236,6 +236,10 @@ func (c *compiler) compileAttributes(attrs *d2graph.Attributes, mk *d2ast.Key) { } else if reserved == "height" { attrs.Height = &d2graph.Scalar{MapKey: mk} } else if reserved == "double-border" { + if attrs.Shape.Value != "" && !strings.EqualFold(attrs.Shape.Value, d2target.ShapeSquare) && !strings.EqualFold(attrs.Shape.Value, d2target.ShapeRectangle) && !strings.EqualFold(attrs.Shape.Value, d2target.ShapeCircle) && !strings.EqualFold(attrs.Shape.Value, d2target.ShapeOval) { + c.errorf(mk.Range.Start, mk.Range.End, `key "double-border" can only be applied to squares, rectangles, circles, ovals`) + return + } attrs.Style.DoubleBorder = &d2graph.Scalar{MapKey: mk} } } diff --git a/d2renderers/d2sketch/sketch.go b/d2renderers/d2sketch/sketch.go index 5aea26fcc..0d7659a79 100644 --- a/d2renderers/d2sketch/sketch.go +++ b/d2renderers/d2sketch/sketch.go @@ -108,6 +108,47 @@ func Rect(r *Runner, shape d2target.Shape) (string, error) { return output, nil } +func DoubleRect(r *Runner, shape d2target.Shape) (string, error) { + jsBigRect := fmt.Sprintf(`node = rc.rectangle(0, 0, %d, %d, { + fill: "%s", + stroke: "%s", + strokeWidth: %d, + %s + });`, shape.Width, shape.Height, shape.Fill, shape.Stroke, shape.StrokeWidth, baseRoughProps) + pathsBigRect, err := computeRoughPaths(r, jsBigRect) + if err != nil { + return "", err + } + jsSmallRect := fmt.Sprintf(`node = rc.rectangle(0, 0, %d, %d, { + fill: "%s", + stroke: "%s", + strokeWidth: %d, + %s + });`, shape.Width-10, shape.Height-10, shape.Fill, shape.Stroke, shape.StrokeWidth, baseRoughProps) + pathsSmallRect, err := computeRoughPaths(r, jsSmallRect) + if err != nil { + return "", err + } + output := "" + for _, p := range pathsBigRect { + output += fmt.Sprintf( + ``, + shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape), + ) + } + for _, p := range pathsSmallRect { + output += fmt.Sprintf( + ``, + shape.Pos.X+5, shape.Pos.Y+5, p, shapeStyle(shape), + ) + } + output += fmt.Sprintf( + ``, + shape.Pos.X, shape.Pos.Y, shape.Width, shape.Height, + ) + return output, nil +} + func Oval(r *Runner, shape d2target.Shape) (string, error) { js := fmt.Sprintf(`node = rc.ellipse(%d, %d, %d, %d, { fill: "%s", @@ -146,22 +187,22 @@ func DoubleOval(r *Runner, shape d2target.Shape) (string, error) { strokeWidth: %d, %s });`, shape.Width/2, shape.Height/2, shape.Width-15, shape.Height-15, shape.Fill, shape.Stroke, shape.StrokeWidth, baseRoughProps) - paths_big_circle, err := computeRoughPaths(r, jsBigCircle) + pathsBigCircle, err := computeRoughPaths(r, jsBigCircle) if err != nil { return "", err } - paths_small_circle, err := computeRoughPaths(r, jsSmallCircle) + pathsSmallCircle, err := computeRoughPaths(r, jsSmallCircle) if err != nil { return "", err } output := "" - for _, p := range paths_big_circle { + for _, p := range pathsBigCircle { output += fmt.Sprintf( ``, shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape), ) } - for _, p := range paths_small_circle { + for _, p := range pathsSmallCircle { output += fmt.Sprintf( ``, shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape), diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 625b24ccd..efba73bdd 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -698,19 +698,40 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske if targetShape.ThreeDee { 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) - } - if sketchRunner != nil { - out, err := d2sketch.Rect(sketchRunner, targetShape) - if err != nil { - return "", err + if !targetShape.DoubleBorder { + if targetShape.Multiple { + fmt.Fprintf(writer, ``, + targetShape.Pos.X+10, targetShape.Pos.Y-10, targetShape.Width, targetShape.Height, style) + } + if sketchRunner != nil { + out, err := d2sketch.Rect(sketchRunner, targetShape) + if err != nil { + return "", err + } + fmt.Fprintf(writer, out) + } else { + fmt.Fprintf(writer, ``, + targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) } - fmt.Fprintf(writer, out) } else { - fmt.Fprintf(writer, ``, - targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) + if targetShape.Multiple { + fmt.Fprintf(writer, ``, + targetShape.Pos.X+10, targetShape.Pos.Y-10, targetShape.Width, targetShape.Height, style) + fmt.Fprintf(writer, ``, + targetShape.Pos.X+15, targetShape.Pos.Y-5, targetShape.Width-10, targetShape.Height-10, style) + } + if sketchRunner != nil { + out, err := d2sketch.DoubleRect(sketchRunner, targetShape) + if err != nil { + return "", err + } + fmt.Fprintf(writer, out) + } else { + fmt.Fprintf(writer, ``, + targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) + fmt.Fprintf(writer, ``, + targetShape.Pos.X+5, targetShape.Pos.Y+5, targetShape.Width-10, targetShape.Height-10, style) + } } } case d2target.ShapeText, d2target.ShapeCode: