1 package shape
2
3 import (
4 "math"
5
6 "oss.terrastruct.com/d2/lib/geo"
7 "oss.terrastruct.com/d2/lib/svg"
8 "oss.terrastruct.com/util-go/go2"
9 )
10
11
12 const CLOUD_WIDE_INNER_X = 0.085
13 const CLOUD_WIDE_INNER_Y = 0.409
14 const CLOUD_WIDE_INNER_WIDTH = 0.819
15 const CLOUD_WIDE_INNER_HEIGHT = 0.548
16 const CLOUD_WIDE_ASPECT_BOUNDARY = (1 + CLOUD_WIDE_INNER_WIDTH/CLOUD_WIDE_INNER_HEIGHT) / 2
17
18
19 const CLOUD_TALL_INNER_X = 0.228
20 const CLOUD_TALL_INNER_Y = 0.179
21 const CLOUD_TALL_INNER_WIDTH = 0.549
22 const CLOUD_TALL_INNER_HEIGHT = 0.820
23 const CLOUD_TALL_ASPECT_BOUNDARY = (1 + CLOUD_TALL_INNER_WIDTH/CLOUD_TALL_INNER_HEIGHT) / 2
24
25
26 const CLOUD_SQUARE_INNER_X = 0.167
27 const CLOUD_SQUARE_INNER_Y = 0.335
28 const CLOUD_SQUARE_INNER_WIDTH = 0.663
29 const CLOUD_SQUARE_INNER_HEIGHT = 0.663
30
31 type shapeCloud struct {
32 *baseShape
33 innerBoxAspectRatio *float64
34 }
35
36 func NewCloud(box *geo.Box) Shape {
37 shape := shapeCloud{
38 baseShape: &baseShape{
39 Type: CLOUD_TYPE,
40 Box: box,
41 },
42 innerBoxAspectRatio: go2.Pointer(0.),
43 }
44 shape.FullShape = go2.Pointer(Shape(shape))
45 return shape
46 }
47
48 func (s shapeCloud) GetInnerBox() *geo.Box {
49 if s.innerBoxAspectRatio != nil && *s.innerBoxAspectRatio != 0. {
50 return s.GetInnerBoxForContent(*s.innerBoxAspectRatio, 1)
51 } else {
52 return s.GetInnerBoxForContent(s.Box.Width, s.Box.Height)
53 }
54 }
55
56
57 func (s shapeCloud) GetInnerBoxForContent(width, height float64) *geo.Box {
58 insideTL := s.GetInsidePlacement(width, height, 0, 0)
59 aspectRatio := width / height
60
61 width, height = s.Box.Width, s.Box.Height
62 if aspectRatio > CLOUD_WIDE_ASPECT_BOUNDARY {
63 width *= CLOUD_WIDE_INNER_WIDTH
64 height *= CLOUD_WIDE_INNER_HEIGHT
65 } else if aspectRatio < CLOUD_TALL_ASPECT_BOUNDARY {
66 width *= CLOUD_TALL_INNER_WIDTH
67 height *= CLOUD_TALL_INNER_HEIGHT
68 } else {
69 width *= CLOUD_SQUARE_INNER_WIDTH
70 height *= CLOUD_SQUARE_INNER_HEIGHT
71 }
72 return geo.NewBox(&insideTL, width, height)
73 }
74
75 func (s shapeCloud) SetInnerBoxAspectRatio(aspectRatio float64) {
76
77 *s.innerBoxAspectRatio = aspectRatio
78 }
79
80 func (s shapeCloud) GetDimensionsToFit(width, height, paddingX, paddingY float64) (float64, float64) {
81 width += paddingX
82 height += paddingY
83 aspectRatio := width / height
84
85 if aspectRatio > CLOUD_WIDE_ASPECT_BOUNDARY {
86 return math.Ceil(width / CLOUD_WIDE_INNER_WIDTH), math.Ceil(height / CLOUD_WIDE_INNER_HEIGHT)
87 } else if aspectRatio < CLOUD_TALL_ASPECT_BOUNDARY {
88 return math.Ceil(width / CLOUD_TALL_INNER_WIDTH), math.Ceil(height / CLOUD_TALL_INNER_HEIGHT)
89 } else {
90 return math.Ceil(width / CLOUD_SQUARE_INNER_WIDTH), math.Ceil(height / CLOUD_SQUARE_INNER_HEIGHT)
91 }
92 }
93
94 func (s shapeCloud) GetInsidePlacement(width, height, paddingX, paddingY float64) geo.Point {
95 r := s.Box
96 width += paddingX
97 height += paddingY
98 aspectRatio := width / height
99 if aspectRatio > CLOUD_WIDE_ASPECT_BOUNDARY {
100 return *geo.NewPoint(r.TopLeft.X+math.Ceil(r.Width*CLOUD_WIDE_INNER_X+paddingX/2), r.TopLeft.Y+math.Ceil(r.Height*CLOUD_WIDE_INNER_Y+paddingY/2))
101 } else if aspectRatio < CLOUD_TALL_ASPECT_BOUNDARY {
102 return *geo.NewPoint(r.TopLeft.X+math.Ceil(r.Width*CLOUD_TALL_INNER_X+paddingX/2), r.TopLeft.Y+math.Ceil(r.Height*CLOUD_TALL_INNER_Y+paddingY/2))
103 } else {
104 return *geo.NewPoint(r.TopLeft.X+math.Ceil(r.Width*CLOUD_SQUARE_INNER_X+paddingX/2), r.TopLeft.Y+math.Ceil(r.Height*CLOUD_SQUARE_INNER_Y+paddingY/2))
105 }
106 }
107
108 func cloudPath(box *geo.Box) *svg.SvgPathContext {
109 pc := svg.NewSVGPathContext(box.TopLeft, box.Width/834, box.Height/523)
110
111 pc.StartAt(pc.Absolute(137.833, 182.833))
112 pc.C(true, 0, 5.556, -5.556, 11.111, -11.111, 11.111)
113 pc.C(true, -70.833, 6.944, -126.389, 77.778, -126.389, 163.889)
114 pc.C(true, 0, 91.667, 62.5, 165.278, 141.667, 165.278)
115 pc.H(true, 537.5)
116 pc.C(true, 84.723, 0, 154.167, -79.167, 154.167, -175)
117 pc.C(true, 0, -91.667, -63.89, -168.056, -144.444, -173.611)
118 pc.C(true, -5.556, 0, -11.111, -4.167, -12.5, -11.111)
119 pc.C(true, -18.056, -93.055, -101.39, -162.5, -198.611, -162.5)
120 pc.C(true, -63.889, 0, -120.834, 29.167, -156.944, 75)
121 pc.C(true, -4.167, 5.556, -11.111, 6.945, -15.278, 5.556)
122 pc.C(true, -13.889, -5.556, -29.166, -8.333, -45.833, -8.333)
123 pc.C(false, 196.167, 71.722, 143.389, 120.333, 137.833, 182.833)
124 pc.Z()
125 return pc
126 }
127
128 func (s shapeCloud) Perimeter() []geo.Intersectable {
129 return cloudPath(s.Box).Path
130 }
131
132 func (s shapeCloud) GetSVGPathData() []string {
133 return []string{
134 cloudPath(s.Box).PathData(),
135 }
136 }
137
138 func (s shapeCloud) GetDefaultPadding() (paddingX, paddingY float64) {
139 return defaultPadding, defaultPadding / 2
140 }
141
View as plain text