Merge pull request #2397 from alixander/c4-person

`shape: c4-person`
This commit is contained in:
Alexander Wang 2025-03-02 10:14:37 -08:00 committed by GitHub
commit 42e11d1e63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 2913 additions and 1 deletions

View file

@ -3,7 +3,9 @@
- Icons: connections can include icons [#12](https://github.com/terrastruct/d2/issues/12)
- Syntax: `suspend`/`unsuspend` to define models and instantiate them [#2394](https://github.com/terrastruct/d2/pull/2394)
- Globs: support for filtering edges based on properties of endpoint nodes (e.g., `&src.style.fill: blue`) [#2395](https://github.com/terrastruct/d2/pull/2395)
- Render: markdown, latex, and code can be used as object labels [#2204](https://github.com/terrastruct/d2/pull/2204/files)
- Render:
- markdown, latex, and code can be used as object labels [#2204](https://github.com/terrastruct/d2/pull/2204)
- `shape: c4-person` to render a person shape like what the C4 model prescribes [#2397](https://github.com/terrastruct/d2/pull/2397)
#### Improvements 🧹

View file

@ -933,6 +933,7 @@ const (
ShapeCallout = "callout"
ShapeStoredData = "stored_data"
ShapePerson = "person"
ShapeC4Person = "c4-person"
ShapeDiamond = "diamond"
ShapeOval = "oval"
ShapeCircle = "circle"
@ -960,6 +961,7 @@ var Shapes = []string{
ShapeCallout,
ShapeStoredData,
ShapePerson,
ShapeC4Person,
ShapeDiamond,
ShapeOval,
ShapeCircle,
@ -1028,6 +1030,7 @@ var DSL_SHAPE_TO_SHAPE_TYPE = map[string]string{
ShapeCallout: shape.CALLOUT_TYPE,
ShapeStoredData: shape.STORED_DATA_TYPE,
ShapePerson: shape.PERSON_TYPE,
ShapeC4Person: shape.C4_PERSON_TYPE,
ShapeDiamond: shape.DIAMOND_TYPE,
ShapeOval: shape.OVAL_TYPE,
ShapeCircle: shape.CIRCLE_TYPE,

View file

@ -0,0 +1,502 @@
{
"name": "",
"config": {
"sketch": false,
"themeID": 0,
"darkThemeID": null,
"pad": null,
"center": null,
"layoutEngine": null
},
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "c4mdperson",
"type": "c4-person",
"pos": {
"x": 0,
"y": 544
},
"width": 312,
"height": 350,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "## Personal Banking Customer\n\n[person]\n\nA customer of the bank, with\\\npersonal bank accounts",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "markdown",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 287,
"labelHeight": 143,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "c4person",
"type": "c4-person",
"pos": {
"x": 244,
"y": 0
},
"width": 135,
"height": 152,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "C4 Style Person",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 110,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "regular_person",
"type": "person",
"pos": {
"x": 89,
"y": 339
},
"width": 134,
"height": 89,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B3",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "Standard Person",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 119,
"labelHeight": 21,
"labelPosition": "OUTSIDE_BOTTOM_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "styling",
"type": "rectangle",
"pos": {
"x": 362,
"y": 293
},
"width": 210,
"height": 546,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "styling",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 78,
"labelHeight": 36,
"labelPosition": "OUTSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "styling.c4styled",
"type": "c4-person",
"pos": {
"x": 425,
"y": 323
},
"width": 85,
"height": 121,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c4styled",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 60,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "styling.c4sized",
"type": "c4-person",
"pos": {
"x": 392,
"y": 629
},
"width": 150,
"height": 180,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "Custom Size",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 87,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [
{
"id": "(regular_person -> c4mdperson)[0]",
"src": "regular_person",
"srcArrow": "none",
"dst": "c4mdperson",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 156,
"y": 454
},
{
"x": 156,
"y": 486
},
{
"x": 156,
"y": 502.79998779296875
},
{
"x": 156,
"y": 538
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c4person -> regular_person)[0]",
"src": "c4person",
"srcArrow": "none",
"dst": "regular_person",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "Compare shapes",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 111,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 245,
"y": 135
},
{
"x": 173.8000030517578,
"y": 197
},
{
"x": 156,
"y": 286.20001220703125
},
{
"x": 156,
"y": 339
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c4person -> styling.c4styled)[0]",
"src": "c4person",
"srcArrow": "none",
"dst": "styling.c4styled",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 379,
"y": 135
},
{
"x": 449.3999938964844,
"y": 197
},
{
"x": 467,
"y": 283.6000061035156
},
{
"x": 467,
"y": 326
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "styling.(c4styled -> c4sized)[0]",
"src": "styling.c4styled",
"srcArrow": "none",
"dst": "styling.c4sized",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 467,
"y": 445
},
{
"x": 467,
"y": 484.20001220703125
},
{
"x": 467,
"y": 520.7990112304688
},
{
"x": 467,
"y": 628
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -0,0 +1,482 @@
{
"name": "",
"config": {
"sketch": false,
"themeID": 0,
"darkThemeID": null,
"pad": null,
"center": null,
"layoutEngine": null
},
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "c4mdperson",
"type": "c4-person",
"pos": {
"x": 12,
"y": 975
},
"width": 312,
"height": 350,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "## Personal Banking Customer\n\n[person]\n\nA customer of the bank, with\\\npersonal bank accounts",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "markdown",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 287,
"labelHeight": 143,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "c4person",
"type": "c4-person",
"pos": {
"x": 201,
"y": 12
},
"width": 135,
"height": 152,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "C4 Style Person",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 110,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "regular_person",
"type": "person",
"pos": {
"x": 101,
"y": 790
},
"width": 134,
"height": 89,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B3",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "Standard Person",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 119,
"labelHeight": 21,
"labelPosition": "OUTSIDE_BOTTOM_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "styling",
"type": "rectangle",
"pos": {
"x": 244,
"y": 249
},
"width": 250,
"height": 471,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "styling",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 78,
"labelHeight": 36,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "styling.c4styled",
"type": "c4-person",
"pos": {
"x": 327,
"y": 299
},
"width": 85,
"height": 121,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c4styled",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 60,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "styling.c4sized",
"type": "c4-person",
"pos": {
"x": 294,
"y": 490
},
"width": 150,
"height": 180,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 1,
"borderRadius": 0,
"fill": "#08427b",
"stroke": "black",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "Custom Size",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "white",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 87,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [
{
"id": "(regular_person -> c4mdperson)[0]",
"src": "regular_person",
"srcArrow": "none",
"dst": "c4mdperson",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 168,
"y": 905
},
{
"x": 168,
"y": 969
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c4person -> regular_person)[0]",
"src": "c4person",
"srcArrow": "none",
"dst": "regular_person",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "Compare shapes",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 111,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 246,
"y": 165
},
{
"x": 246.25,
"y": 204
},
{
"x": 168,
"y": 204
},
{
"x": 168,
"y": 790
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c4person -> styling.c4styled)[0]",
"src": "c4person",
"srcArrow": "none",
"dst": "styling.c4styled",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 291,
"y": 165
},
{
"x": 291.25,
"y": 204
},
{
"x": 369.5,
"y": 204
},
{
"x": 370,
"y": 302
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "styling.(c4styled -> c4sized)[0]",
"src": "styling.c4styled",
"srcArrow": "none",
"dst": "styling.c4sized",
"dstArrow": "triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 369,
"y": 421
},
{
"x": 370,
"y": 489
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -978,3 +978,51 @@ cloud: |md
blah blah
|
-- c4-person-shape --
c4mdperson.shape: c4-person
c4mdperson: |md
## Personal Banking Customer
[person]
A customer of the bank, with\
personal bank accounts
|
c4person: {
shape: c4-person
label: "C4 Style Person"
}
regular_person -> c4mdperson
regular_person: {
shape: person
label: "Standard Person"
}
c4person -> regular_person: "Compare shapes"
styling: {
c4styled: {
shape: c4-person
style.fill: "#91BEEA"
style.stroke: "#2E6195"
style.stroke-width: 2
}
c4sized: {
shape: c4-person
width: 150
height: 180
label: "Custom Size"
}
}
c4person -> styling.c4styled -> styling.c4sized
**: {
&shape: c4-person
style.fill: "#08427b"
style.stroke: black
style.font-color: white
style.stroke-width: 1
}

View file

@ -21,6 +21,7 @@ const (
CALLOUT_TYPE = "Callout"
STORED_DATA_TYPE = "StoredData"
PERSON_TYPE = "Person"
C4_PERSON_TYPE = "c4-person"
DIAMOND_TYPE = "Diamond"
OVAL_TYPE = "Oval"
CIRCLE_TYPE = "Circle"
@ -155,6 +156,8 @@ func NewShape(shapeType string, box *geo.Box) Shape {
return NewParallelogram(box)
case PERSON_TYPE:
return NewPerson(box)
case C4_PERSON_TYPE:
return NewC4Person(box)
case QUEUE_TYPE:
return NewQueue(box)
case REAL_SQUARE_TYPE:

View file

@ -0,0 +1,154 @@
package shape
import (
"math"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/svg"
"oss.terrastruct.com/util-go/go2"
)
// Optimal value for circular arc approximation with cubic bezier curves
const kCircleApprox = 0.5522847498307936 // 4*(math.Sqrt(2)-1)/3
type shapeC4Person struct {
*baseShape
}
func NewC4Person(box *geo.Box) Shape {
shape := shapeC4Person{
baseShape: &baseShape{
Type: C4_PERSON_TYPE,
Box: box,
},
}
shape.FullShape = go2.Pointer(Shape(shape))
return shape
}
const (
C4_PERSON_AR_LIMIT = 1.5
)
func (s shapeC4Person) GetInnerBox() *geo.Box {
width := s.Box.Width
height := s.Box.Height
headRadius := width * 0.22
headCenterY := height * 0.18
bodyTop := headCenterY + headRadius*0.8
tl := s.Box.TopLeft.Copy()
horizontalPadding := width * 0.1
tl.X += horizontalPadding
tl.Y += bodyTop + height*0.05
innerWidth := width - (horizontalPadding * 2)
innerHeight := height - tl.Y + s.Box.TopLeft.Y - (height * 0.05)
return geo.NewBox(tl, innerWidth, innerHeight)
}
func bodyPath(box *geo.Box) *svg.SvgPathContext {
width := box.Width
height := box.Height
pc := svg.NewSVGPathContext(box.TopLeft, 1, 1)
headRadius := width * 0.22
headCenterY := height * 0.18
bodyTop := headCenterY + headRadius*0.8
bodyWidth := width
bodyHeight := height - bodyTop
bodyLeft := 0
cornerRadius := width * 0.175
pc.StartAt(pc.Absolute(float64(bodyLeft), bodyTop+cornerRadius))
pc.C(true, 0, -kCircleApprox*cornerRadius, kCircleApprox*cornerRadius, -cornerRadius, cornerRadius, -cornerRadius)
pc.H(true, bodyWidth-2*cornerRadius)
pc.C(true, kCircleApprox*cornerRadius, 0, cornerRadius, kCircleApprox*cornerRadius, cornerRadius, cornerRadius)
pc.V(true, bodyHeight-2*cornerRadius)
pc.C(true, 0, kCircleApprox*cornerRadius, -kCircleApprox*cornerRadius, cornerRadius, -cornerRadius, cornerRadius)
pc.H(true, -(bodyWidth - 2*cornerRadius))
pc.C(true, -kCircleApprox*cornerRadius, 0, -cornerRadius, -kCircleApprox*cornerRadius, -cornerRadius, -cornerRadius)
pc.Z()
return pc
}
func headPath(box *geo.Box) *svg.SvgPathContext {
width := box.Width
height := box.Height
pc := svg.NewSVGPathContext(box.TopLeft, 1, 1)
headRadius := width * 0.22
headCenterX := width / 2
headCenterY := height * 0.18
pc.StartAt(pc.Absolute(headCenterX, headCenterY-headRadius))
pc.C(false,
headCenterX+headRadius*kCircleApprox, headCenterY-headRadius,
headCenterX+headRadius, headCenterY-headRadius*kCircleApprox,
headCenterX+headRadius, headCenterY)
pc.C(false,
headCenterX+headRadius, headCenterY+headRadius*kCircleApprox,
headCenterX+headRadius*kCircleApprox, headCenterY+headRadius,
headCenterX, headCenterY+headRadius)
pc.C(false,
headCenterX-headRadius*kCircleApprox, headCenterY+headRadius,
headCenterX-headRadius, headCenterY+headRadius*kCircleApprox,
headCenterX-headRadius, headCenterY)
pc.C(false,
headCenterX-headRadius, headCenterY-headRadius*kCircleApprox,
headCenterX-headRadius*kCircleApprox, headCenterY-headRadius,
headCenterX, headCenterY-headRadius)
return pc
}
func (s shapeC4Person) Perimeter() []geo.Intersectable {
width := s.Box.Width
height := s.Box.Height
bodyPerimeter := bodyPath(s.Box).Path
headRadius := width * 0.22
headCenterX := s.Box.TopLeft.X + width/2
headCenterY := s.Box.TopLeft.Y + height*0.18
headCenter := geo.NewPoint(headCenterX, headCenterY)
headEllipse := geo.NewEllipse(headCenter, headRadius, headRadius)
return append(bodyPerimeter, headEllipse)
}
func (s shapeC4Person) GetSVGPathData() []string {
return []string{
bodyPath(s.Box).PathData(),
headPath(s.Box).PathData(),
}
}
func (s shapeC4Person) GetDimensionsToFit(width, height, paddingX, paddingY float64) (float64, float64) {
totalWidth := width + paddingX
totalHeight := height + paddingY
if totalHeight < totalWidth*0.8 {
totalHeight = totalWidth * 0.8
}
totalHeight *= 1.4
totalWidth, totalHeight = LimitAR(totalWidth, totalHeight, C4_PERSON_AR_LIMIT)
return math.Ceil(totalWidth), math.Ceil(totalHeight)
}
func (s shapeC4Person) GetDefaultPadding() (paddingX, paddingY float64) {
return 20, defaultPadding * 1.5
}