lang: Custom shape types
Currently following shape types are supported: circle, oval, hexagon, queue, package, cylinder, page, cloud, class, sql_table.
Support creation of own shape types. This can be then also combined with style and shape:image.
As an example:
# definition
type unicorn:shape {
icon: https://example.com/unicorn.svg
}
# usage
foo: {shape:unicorn}
Yes, this is very important for people to use and share consistent design vocabularies and communicate with them.
Also need custom type definitions for connectors as well.
It would also be great if these custom types could be included or imported some how, so they could be stored and shared separately (is there a feature request for that yet?)
Re, yep! issue for imports: https://github.com/terrastruct/d2/issues/554
Re, this issue: a shape type in D2 needs:
- The SVG path
- "Center ports" that routing should go (otherwise will be treated like a rectangle for routing)
- A bit for whether its aspect ratio needs to stay 1:1 (set to true on square and circle types)
- A bounding box for its labels (e.g. in the cylinder type, the space for labels is not the same as the space for the shape)
Is it custom shapes you want or just avoid repeating the icon definition?
I believe that the class keyword is really what you'd want, which is the works. Anywhere you want to use a custom image, just do foo: { class: unicorn }, and define the class unicorn to have the icon. Maybe you can use that with a shape. Like, your unicorn SVG when combined with shape: circle can center-crop to give a circular image.
If you still believe custom shape types are needed, I'm interested to hear what I might be missing.
for myself, I'm looking for the ability to define a new object in terms of existing shape types plus properties (added or overridden). (Not just a new shape with a new SVG image etc.). If class is similar, that would be great.
(Note I am pretty new to using d2 so may be missing some stuff.)
E.g. instead of:
thing1: {
shape: cloud
icon: https://icons.terrastruct.com/infra/019-network.svg
}
thing2: {
shape: cloud
icon: https://icons.terrastruct.com/infra/019-network.svg
}
obj1: {
shape: diamond
}
obj2: {
shape: rectangle
}
thing1 -> obj1: {
style: {
stroke-dash: 4
}
}
thing2 -> obj1: {
style: {
stroke-dash: 4
}
}
obj1 -> obj2
thing3: {
shape: cloud
icon: https://icons.terrastruct.com/infra/019-network.svg
style: {
fill: yellow
}
}
obj2 -> thing3
Do something like:
def NetSource: {
shape: cloud
icon: https://icons.terrastruct.com/infra/019-network.svg
}
def Update: {
style: {
stroke-dash: 4
}
}
def Combiner: {
shape: diamond
}
def Processor: {
shape: rectangle
}
thing1 (NetSource)
obj1 (Combiner)
obj2 (Processor)
thing1 -> obj1 <Update>
thing2 (NetSource) -> obj1 <Update>
obj1 -> obj2
thing3 (NetSource): {
style: {
fill: yellow
}
}
obj2 -> thing3 <Update>
... or however you want to do it.
oh the aliasing use case is interesting, saying "class: Processor" would achieve the same effect but does have different semantics.
Is it custom shapes you want or just avoid repeating the icon definition?
For me, it is the repeating of the icon in the first place.
However, having custom shapes still has its benefits:
- You can create shape sets and share them with others. Thinking in the direction of draw.io.
- A shape can have an appearance and a behavior. The
classwould only define the appearance.
For me that would be a good addition for the arrow to reach the shape. As When I define an image there is still a gap with the shape. If defining a shape I think it'll be able to connect correctly.
@Nepo26 oh interesting, so the custom shape i guess would hug the path of the image SVG instead of just a rectangle for the image?
Exactly.
Currently what we have is a padded image if we use a class with an svg and an image shape. As is observable in the following.
What I think that would be ideal is to be able to customize this padding. Or when we define a custom shape it works in the same way. Connecting directly as the following:
I was really looking for custom shapes too.
Yes "svg" icons would be useful BUT I really want to be able to superpose text, i.e. a label, without modifying the svg source itself.
e.g. https://play.d2lang.com/?script=ZI5RisQgEET_-xR1gSjks_cwS8d0VDAaVHZZwt59MBkYhvkququoei5Ja9oYJxEB6xxdyScBSRZNJqtUdpq71ukWAlr_S2q2kvvkSiqV8Rti169hBTmUEXfx1z3aGKH3o7G165wke-PKbuPu7Tp_-ypHiM60H_9M35N42xxN_wS8hICtFD4vfL6ph7VI_XzSIwAA__8%3D&
classes: {
d2icon: {
label.near: center-center
style.font-color: white
shape: image
icon: https://d2lang.com/img/d2_graphic.svg
icon.near: center-center
}
}
foo: {class: d2icon}
bar: {class: d2icon}
Another suggestion for expanding the available shapes is to have these options for each shape:
- flipped horizontally
- flipped vertically
- rounded corners
This (especially the flipping) would give many more options without needing to specifically define new shapes.