...

Source file src/oss.terrastruct.com/d2/lib/shape/shape_cloud.go

Documentation: oss.terrastruct.com/d2/lib/shape

     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  // The percentage values of the cloud's wide inner box
    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  // The percentage values of the cloud's tall inner box
    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  // The percentage values of the cloud's square inner box
    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  // we need this since the content's aspect ratio determines which placement is used
    57  func (s shapeCloud) GetInnerBoxForContent(width, height float64) *geo.Box {
    58  	insideTL := s.GetInsidePlacement(width, height, 0, 0)
    59  	aspectRatio := width / height
    60  	// aspect ratio and position are computed with given dimensions, but final box size is determined by shape size
    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  	// only used for cloud
    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  	// use the inner box with the closest aspect ratio (wide, tall, or square box)
    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  	// Note: original path TopLeft=(83, 238), absolute values updated so top left is at 0,0
   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