diff --git a/d2compiler/compile.go b/d2compiler/compile.go
index 9cf579c30..5c370c204 100644
--- a/d2compiler/compile.go
+++ b/d2compiler/compile.go
@@ -235,6 +235,8 @@ func (c *compiler) compileAttributes(attrs *d2graph.Attributes, mk *d2ast.Key) {
attrs.Width = &d2graph.Scalar{MapKey: mk}
} else if reserved == "height" {
attrs.Height = &d2graph.Scalar{MapKey: mk}
+ } else if reserved == "double-border" {
+ attrs.Style.DoubleBorder = &d2graph.Scalar{MapKey: mk}
}
}
diff --git a/d2exporter/export.go b/d2exporter/export.go
index 9706365f0..8a636759e 100644
--- a/d2exporter/export.go
+++ b/d2exporter/export.go
@@ -93,6 +93,9 @@ func applyStyles(shape *d2target.Shape, obj *d2graph.Object) {
if obj.Attributes.Style.Font != nil {
shape.FontFamily = obj.Attributes.Style.Font.Value
}
+ if obj.Attributes.Style.DoubleBorder != nil {
+ shape.DoubleBorder, _ = strconv.ParseBool(obj.Attributes.Style.DoubleBorder.Value)
+ }
}
func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 006b9cb64..334d70595 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -147,6 +147,7 @@ type Style struct {
Italic *Scalar `json:"italic,omitempty"`
Underline *Scalar `json:"underline,omitempty"`
Filled *Scalar `json:"filled,omitempty"`
+ DoubleBorder *Scalar `json:"doubleBorder,omitempty"`
}
func (s *Style) Apply(key, value string) error {
@@ -300,6 +301,15 @@ func (s *Style) Apply(key, value string) error {
return errors.New(`expected "filled" to be true or false`)
}
s.Filled.Value = value
+ case "double-border":
+ if s.DoubleBorder == nil {
+ break
+ }
+ _, err := strconv.ParseBool(value)
+ if err != nil {
+ return errors.New(`expected "double-border" to be true or false`)
+ }
+ s.DoubleBorder.Value = value
default:
return fmt.Errorf("unknown style key: %s", key)
}
@@ -1284,8 +1294,9 @@ var StyleKeywords = map[string]struct{}{
"underline": {},
// Only for shapes
- "shadow": {},
- "multiple": {},
+ "shadow": {},
+ "multiple": {},
+ "double-border": {},
// Only for squares
"3d": {},
diff --git a/d2oracle/edit.go b/d2oracle/edit.go
index b379df0d5..48f7d0692 100644
--- a/d2oracle/edit.go
+++ b/d2oracle/edit.go
@@ -284,6 +284,11 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
attrs.Style.Multiple.MapKey.SetScalar(mk.Value.ScalarBox())
return nil
}
+ case "double-border":
+ if attrs.Style.DoubleBorder != nil {
+ attrs.Style.DoubleBorder.MapKey.SetScalar(mk.Value.ScalarBox())
+ return nil
+ }
case "font":
if attrs.Style.Font != nil {
attrs.Style.Font.MapKey.SetScalar(mk.Value.ScalarBox())
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 8623d2ec6..f2536fe84 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -589,7 +589,7 @@ func renderOval(tl *geo.Point, width, height float64, style string) string {
return fmt.Sprintf(``, cx, cy, rx, ry, style)
}
-func renderDoubleCircle(tl *geo.Point, width, height float64, style string) string {
+func renderDoubleOval(tl *geo.Point, width, height float64, style string) string {
return renderOval(tl, width, height, style) + renderOval(&geo.Point{X: tl.X + 5, Y: tl.Y + 5}, width-10, height-10, style)
}
@@ -764,31 +764,46 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
fmt.Fprintf(writer, closingTag)
return labelMask, nil
case d2target.ShapeOval:
- if targetShape.Multiple {
- fmt.Fprint(writer, renderOval(multipleTL, width, height, style))
- }
- if sketchRunner != nil {
- out, err := d2sketch.Oval(sketchRunner, targetShape)
- if err != nil {
- return "", err
+ if !targetShape.DoubleBorder {
+ if targetShape.Multiple {
+ fmt.Fprint(writer, renderOval(multipleTL, width, height, style))
}
- fmt.Fprintf(writer, out)
- } else {
- fmt.Fprint(writer, renderOval(tl, width, height, style))
- }
- case d2target.ShapeDoubleCircle:
- if targetShape.Multiple {
- fmt.Fprint(writer, renderDoubleCircle(multipleTL, width, height, style))
- }
- if sketchRunner != nil {
- out, err := d2sketch.DoubleOval(sketchRunner, targetShape)
- if err != nil {
- return "", err
+ if sketchRunner != nil {
+ out, err := d2sketch.Oval(sketchRunner, targetShape)
+ if err != nil {
+ return "", err
+ }
+ fmt.Fprintf(writer, out)
+ } else {
+ fmt.Fprint(writer, renderOval(tl, width, height, style))
}
- fmt.Fprintf(writer, out)
} else {
- fmt.Fprint(writer, renderDoubleCircle(tl, width, height, style))
+ if targetShape.Multiple {
+ fmt.Fprint(writer, renderDoubleOval(multipleTL, width, height, style))
+ }
+ if sketchRunner != nil {
+ out, err := d2sketch.DoubleOval(sketchRunner, targetShape)
+ if err != nil {
+ return "", err
+ }
+ fmt.Fprintf(writer, out)
+ } else {
+ fmt.Fprint(writer, renderDoubleOval(tl, width, height, style))
+ }
}
+ // case d2target.ShapeDoubleCircle:
+ // if targetShape.Multiple {
+ // fmt.Fprint(writer, renderDoubleCircle(multipleTL, width, height, style))
+ // }
+ // if sketchRunner != nil {
+ // out, err := d2sketch.DoubleOval(sketchRunner, targetShape)
+ // if err != nil {
+ // return "", err
+ // }
+ // fmt.Fprintf(writer, out)
+ // } else {
+ // fmt.Fprint(writer, renderDoubleCircle(tl, width, height, style))
+ // }
case d2target.ShapeImage:
fmt.Fprintf(writer, ``,
diff --git a/d2target/d2target.go b/d2target/d2target.go
index f810f7cce..75113ec90 100644
--- a/d2target/d2target.go
+++ b/d2target/d2target.go
@@ -143,9 +143,10 @@ type Shape struct {
Fill string `json:"fill"`
Stroke string `json:"stroke"`
- Shadow bool `json:"shadow"`
- ThreeDee bool `json:"3d"`
- Multiple bool `json:"multiple"`
+ Shadow bool `json:"shadow"`
+ ThreeDee bool `json:"3d"`
+ Multiple bool `json:"multiple"`
+ DoubleBorder bool `json:"double-border"`
Tooltip string `json:"tooltip"`
Link string `json:"link"`
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index c527928b8..0fa299c0d 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -42,14 +42,13 @@ oval: {shape: "oval"}
circle: {shape: "circle"}
hexagon: {shape: "hexagon"}
cloud: {shape: "cloud"}
-double_circle: {shape: "double_circle"}
rectangle -> square -> page
parallelogram -> document -> cylinder
queue -> package -> step
callout -> stored_data -> person
diamond -> oval -> circle
-hexagon -> cloud -> double_circle
+hexagon -> cloud
`,
},
{
@@ -72,14 +71,13 @@ oval: {shape: "oval"}
circle: {shape: "circle"}
hexagon: {shape: "hexagon"}
cloud: {shape: "cloud"}
-double_circle: {shape: "double_circle"}
rectangle -> square -> page
parallelogram -> document -> cylinder
queue -> package -> step
callout -> stored_data -> person
diamond -> oval -> circle
-hexagon -> cloud -> double_circle
+hexagon -> cloud
rectangle.multiple: true
square.multiple: true
@@ -98,7 +96,6 @@ oval.multiple: true
circle.multiple: true
hexagon.multiple: true
cloud.multiple: true
-double_circle.multiple: true
`,
},
{
@@ -121,14 +118,13 @@ oval: {shape: "oval"}
circle: {shape: "circle"}
hexagon: {shape: "hexagon"}
cloud: {shape: "cloud"}
-double_circle: {shape: "double_circle"}
rectangle -> square -> page
parallelogram -> document -> cylinder
queue -> package -> step
callout -> stored_data -> person
diamond -> oval -> circle
-hexagon -> cloud -> double_circle
+hexagon -> cloud
rectangle.shadow: true
square.shadow: true
@@ -147,7 +143,6 @@ oval.shadow: true
circle.shadow: true
hexagon.shadow: true
cloud.shadow: true
-double_circle.shadow: true
`,
},
{
diff --git a/lib/shape/shape.go b/lib/shape/shape.go
index edfc5e6f3..6422c588f 100644
--- a/lib/shape/shape.go
+++ b/lib/shape/shape.go
@@ -24,7 +24,6 @@ const (
CIRCLE_TYPE = "Circle"
HEXAGON_TYPE = "Hexagon"
CLOUD_TYPE = "Cloud"
- DOUBLE_CIRCLE_TYPE = "DoubleCircle"
TABLE_TYPE = "Table"
CLASS_TYPE = "Class"
@@ -109,8 +108,6 @@ func NewShape(shapeType string, box *geo.Box) Shape {
return NewCallout(box)
case CIRCLE_TYPE:
return NewCircle(box)
- case DOUBLE_CIRCLE_TYPE:
- return NewDoubleCircle(box)
case CLASS_TYPE:
return NewClass(box)
case CLOUD_TYPE:
diff --git a/lib/shape/shape_double_circle.go b/lib/shape/shape_double_circle.go
deleted file mode 100644
index f67e18b09..000000000
--- a/lib/shape/shape_double_circle.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package shape
-
-import (
- "math"
-
- "oss.terrastruct.com/d2/lib/geo"
-)
-
-type shapeDoubleCircle struct {
- *baseShape
-}
-
-func NewDoubleCircle(box *geo.Box) Shape {
- return shapeDoubleCircle{
- baseShape: &baseShape{
- Type: DOUBLE_CIRCLE_TYPE,
- Box: box,
- },
- }
-}
-
-func (s shapeDoubleCircle) AspectRatio1() bool {
- return true
-}
-
-func (s shapeDoubleCircle) GetDimensionsToFit(width, height, padding float64) (float64, float64) {
- radius := math.Ceil(math.Sqrt(math.Pow(width/2, 2)+math.Pow(height/2, 2))) + padding
- return radius * 2, radius * 2
-}
-
-func (s shapeDoubleCircle) GetInsidePlacement(width, height, padding float64) geo.Point {
- return *geo.NewPoint(s.Box.TopLeft.X+math.Ceil(s.Box.Width/2-width/2), s.Box.TopLeft.Y+math.Ceil(s.Box.Height/2-height/2))
-}
-
-func (s shapeDoubleCircle) Perimeter() []geo.Intersectable {
- return []geo.Intersectable{geo.NewEllipse(s.Box.Center(), s.Box.Width/2, s.Box.Height/2)}
-}