Lucidchart and Lucidspark support using custom, user generated shapes. Custom shapes can be made in extensions or with the in browser LCSZ editor. Both will produce .lcsz files and code written for one can be used in the other. This page covers the creation and testing of custom shapes.
The Extension API allows users and developers to build custom shape libraries that can be distributed to select Lucid users, to all users in their Lucid account, or to all Lucid users via the Lucid Marketplace. This page also describes Lucid’s custom shape format, which provides users and developers a powerful set of capabilities for creating and customizing shapes.
The purpose of this documentation is to describe the basic functionality, format and validation of the custom shape format, sufficient to allow a user to build a custom shape library from scratch. The format is continuously evolving (particularly with feedback being solicited); it is expected that this documentation will be revised over time to reflect the most up-to-date information as it relates to the custom shape format.
In general, concepts and features listed in this document are implemented and working, however there are a few areas which are in development and are not considered working:
Creating a shape library with the online editor will result in a .lcsz file (a zip file with a renamed extension) which will represent a single shape library. An extension can contain multiple shape libraries or none at all. Creating a shape library will be covered below. The online editor is only ever editing a single shape library.
Each shape contains two parts. The first part is a shape definition, in the extension API this is a single .shape file. The second part is the shape library entry - a listing in the library manifest (extension api) or in the list on the shapes tab (online editor). The shape library entry will reference the shape definition and also define the name and any defaults for that shape. A single shape definition can be used in multiple shape library entries.
All code files in the custom shape format allow HJSON, a superset of JSON which allows comments, unquoted object keys, and flexibility around commas.
{
// No quotes on the value below.
a: b
c: ["d", "e", "f"]
g: 123
}
Note the missing trailing commas and the unquoted string value for "b". The unquoted string value for "b" illustrates a potential gotcha in HJSON, where commas or other characters (anything after the ":") will be included in the string. For example, the following would actually represent an object with the key/value pair of "a" -> "b, }" instead of "a" -> "b" (and would be a syntax error):
For more information about the HJSON format, see https://hjson.org/.
When you open up the LCSZ editor you are starting with a blank library. On the left there are 3 tabs: Shapes, Definitions, and Images. Every shape needs a definition, navigate to the Definitions tab and click the + button. Fill in the code with a simple rectangle
{
geometry:
[
{
type: rect
}
]
}
More complex geometries will be covered later.
Now you can navigate to the shape tab and click +. On the right panel you can set the name of the shape and select the definition you want to reference. Then set any defaults you want to. The default shape will be rendered to the center screen.
Shape libraries are exported and imported by downloading and uploading .lcsz files with the buttons on the top right. The only way to save a library you are in the process of making is to export the .lcsz file and import it later.
A shape library is defined in a folder that lives in an extension package. An extension package is a set of code, content, and configuration for Lucid products that is installable by individual Lucid users or by a Lucid account admin for use by their entire account. Extension packages can be distributed via Lucid’s Developer Portal, and can have one or more shape libraries. Instructions for creating extension packages can be found in the Extension API documentation.
To create a shape library, use this command inside an extension package directory:
npx lucid-package create-shape-library <name>
This will create a shape library with one custom shape in it (just a rectangle) and a default
For creating a library just from images see create-image-shape-library.
Any time you are running
Those shapes are kept up to date live as you make code changes to the shape definitions, including updating any of those shapes that are on-canvas. This does not happen for released extensions, it is only a convenience while developing locally.
In the new shape library directory there will be a folder
The library.manifest file defines what shapes are included in the shape library. Only shape entries which are defined in the manifest file will be shown to the user. The manifest file only defines what shapes exist in the library and what the default settings for that shape are. All the other details about the shape are defined in the referenced shape definition file.
A single shape definition can be referenced in multiple shape entries. Any shape definitions not referenced in the manifest file will not be imported.
The manifest file is an HJSON-formatted file which defines the library name and the list of shapes. Each shape must specify the shape ID (which is the base filename of the shape in the shapes folder, for example "shape1" for the file /shapes/shape1.shape).
The manifest file also exists in a library exported from the online editor and could be further edited, but does not have to be written by hand.
{
"name": "Test Library",
"shapes": [
{
"shape": "first",
"name": "Test Shape",
"defaults": {
"fillColor": "#ff0000",
"strokeColor": "#00ffff",
"strokeWidth": 3,
"width": 300,
"height": 300
}
}
]
}
Property | Description |
---|---|
|
string or TranslatableString The name of the shape library |
|
ShapeEntry[] The list of shapes in this shape library. Only shapes in this list are shown |
Property | Description |
---|---|
|
string The basename for the shape file, found in |
|
string (optional) If specified, can be used to differentiate between multiple shape entries with the same |
|
string or TranslatableString The name displayed for this shape in the shape library |
|
ShapeDefaults Default block properties, like width, height, fill, etc. |
|
Default values for custom shape data, defined in the shape definition |
Property | Description |
---|---|
|
number The initial shape width |
|
number The initial shape height |
|
The initial shape rounding |
|
The initial fill color for the shape |
|
The initial line color for the shape |
|
The initial line width for the shape |
|
The initial opacity for the shape |
|
The initial rotation for the shape |
|
fixed aspect ratio. See also |
|
Starting url link on the shape |
Property | Description |
---|---|
|
string The fallback value to display if the i18n value is not found |
|
string The i18n key from the i18n files |
You can create instances of these shapes programmatically as well. Here is a simple example of placing a shape named "my-shape" in the library named "my-library" onto the canvas, then setting a data value on it:
client.registerAction('test', async () => {
const page = viewport.getCurrentPage();
if (page) {
const def = await client.getCustomShapeDefinition('my-library', 'my-shape');
if (def) {
def.boundingBox.x = 500;
def.boundingBox.y = 500;
const block = page.addBlock(def);
block.shapeData.set('Value', 50);
}
}
});
A shape definition describes the components needed to render a shape. At the most basic level, a shape definition provides the geometry of the shape, which allows Lucidchart to produce path data. More complex shapes can contain sub-shapes that each have their own unique geometry, and sub-shapes of their own.
Shape definitions are defined in
The manifest file defines the name and defaults for your shape. The available defaults are shown to the right. The manifest file will exist in the output .lcsz file from the online editor but does not need to be directly edited.
Setting the aspect ratio will lock the shape to that ratio, but the user can unlock it. To prevent the user from unlocking the aspect ratio include "AspectRatio" under the list "locked" in the shape file.
Many properties on a shape might be inherited from the defaults on a document (i.e. if there is a default rounding of 6). If you do not want to inherit these properties you must set them in the manifest file.
{
"name": "Test Library",
"shapes": [
{
"shape": "first",
"name": "Test Shape",
"defaults": {
"aspectRatio": .5,
"fillColor": "#ff0000",
"link": "your.url.com",
"opacity": .5,
"rotation": 1.5,
"strokeColor": "#00ffff",
"strokeWidth": 3,
"height": 300,
"width": 600
}
}
]
}
Shape files are defined as a json file.
"locked" is a list of fields to lock. The only supported fields are ["aspectRatio", "verticalResize", "horizontalResize"]. If you lock the aspect ratio without setting a default it will use the width and height to calculate it.
{
"locked": ["aspectRatio"],
"images": {
"imageReference": {
"path": "file",
"type": "file",
},
},
"geometry": [
{
"type": "rect",
},
],
"style": {
"fill": {
"type": "image",
"ref": "imageReference",
"mode": "stretch",
},
},
}
A shape's geometry, produced by the components in the shape definition, is what creates the data rendered to the screen, allowing Lucidchart to draw the shape.
Geometry can be created from a standard geometric shape, a custom path, or a combination of the two (achieved with Boolean operations).
Geometry is drawn using relative coordinates which are defined based on the bounds of the sub-shape that the geometry is contained in (or using the rectangle defined by the main shape if it is top-level geometry).
For complex geometry, formulas can be used to determine whether or not to include the geometry in the shape's render output (or in Boolean operations). Conditional geometry evaluates the formula and if the result is false, the geometry is not rendered (or included in Boolean operations).
All geometry defined in the custom shape format follows the general schema:
{
"type": "rect",
"repeat": {
"min": 5,
"max": 20
},
"localFill": false
}
Property | Description |
---|---|
|
string The type of geometry Options: rect, ellipse, polygon, path, union, intersection, difference, xor |
|
A formula which controls whether or not the geometry is included in the output |
|
|
|
Defines whether or not this geometry starts a gradient |
|
Allows local definition of formula (see Definitions below) |
Property | Description |
---|---|
|
string |
|
formula |
Complex geometry consisting of multiple of the same shape with different positions or sizes can use formulas to repeat the geometry multiple times.
There are two types of ways to use repeating geometry, using For Repeats or Map Repeats.
For Repeats are used to repeat geometry a number of times where an index variable is set from a starting number to an ending number (taking an optional step value). For example, a for repeat from 1 to 7 with a step of 2 would loops 4 times, using the values of 1, 3, 5, and 7, sequentially.
Property | Description |
---|---|
|
|
|
number or formula |
|
number or formula |
|
|
Map Repeats are used to repeat geometry by looping over the items in the supplied array. For example, if the value for the array was [2, 4, 6, 8], the geometry would repeat 4 times with the variable defined for index (if present) set to 1, 2, 3, and 4, and the variable set for value to 2, 4, 6, and 8, respectively.
Property | Description |
---|---|
|
|
|
number or formula |
|
number or formula |
|
|
Property | Description |
---|---|
|
'rect' |
|
|
|
|
|
|
|
|
|
The x-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The y-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The width of this rectangle, relative to its bounds (default: 1) |
|
The height of this rectangle, relative to its bounds (default: 1) |
{
type: "rect"
// NOTE: Unspecified x and y default to 0.
// NOTE: Unspecified w and h default to 1.
}
{
type: "rect",
x: 0.5,
y: 0.5,
w: 0.5,
h: 0.5
}
Property | Description |
---|---|
|
'ellipse' |
|
|
|
|
|
|
|
|
|
The x-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The y-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The width of this rectangle, relative to its bounds (default: 1) |
|
The height of this rectangle, relative to its bounds (default: 1) |
{
type: "ellipse"
}
Property | Description |
---|---|
|
'polygon' |
|
|
|
|
|
|
|
|
|
number or formula The number of sides of this polygon |
|
The x-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The y-coordinate of this rectangle, relative to its bounds (default: 0) |
|
The width of this rectangle, relative to its bounds (default: 1) |
|
The height of this rectangle, relative to its bounds (default: 1) |
{
type: "polygon"
n: 6
}
{
type: "polygon",
n: 5,
inset: 0.5,
w: 0.5,
h: 0.5
}
Property | Description |
---|---|
|
'path' |
|
|
|
|
|
|
|
|
|
interpolated string The defined path of the Path geometry. Uses SVG paths and assumes relative coordinates. |
{
type: "path",
path: "M 0 0 L 1 1 L 0 1 Z"
}
You can learn more about SVG paths online
Boolean geometry operations require the geometry it operates on to be defined:
Property | Description |
---|---|
|
'union', 'intersection', 'difference', 'xor' |
|
|
|
|
|
|
|
|
|
Geometry[] The geometry used in the Boolean operation |
{
type: "union",
geometry: [
{ type: "ellipse",
x: 0.25,
w: 0.75,
h: 0.75
}
{ type: "ellipse",
y: 0.25,
w: 0.75,
h: 0.75
}
]
}
{
type: "intersection",
geometry: [
{ type: "ellipse",
x: 0.25,
w: 0.75,
h: 0.75
}
{ type: "ellipse",
y: 0.25,
w: 0.75,
h: 0.75
}
]
}
{
type: "difference",
geometry: [
{ type: "ellipse",
x: 0.25,
w: 0.75,
h: 0.75
}
{ type: "ellipse",
y: 0.25,
w: 0.75,
h: 0.75
}
]
}
{
type: "xor",
geometry: [
{ type: "ellipse",
x: 0.25,
w: 0.75,
h: 0.75
}
{ type: "ellipse",
y: 0.25,
w: 0.75,
h: 0.75
}
]
}
Geometry is rendered using a styling defined within the shape definition or the sub-shape. The highest-level style is used unless it is overridden by a lower-level style. So, for example, if the style is only defined in the top-level of the shape definition, all geometry in sub-shapes would be rendered using that same style.
The style defines the formatting used to render the sub-shape's geometry. There are 3 main aspects of the style formatting that can be changed:
Type | Specifics |
---|---|
Stroke | Specifies the geometries' line color and line size. |
Fill | Specifies the fill type of the geometries. This can be a solid color (type: color), a gradient (type: linear-gradient or type: radial-gradient), or an image fill (type: image). |
Rounding | Specifies rounding that is applied to the geometries when rendering. A rectangle with rounding set will be rendered as a rounded rectangle. |
Images referenced in image fills must be added into the shape's image map within the shape definition in order to identify where the image file comes from (either a URL or internal image added to the LCSZ file).
The image map is a collection of names which reference an image file or URL; the names are then referenced within the image fills themselves.
Under a shape definition you can add a new field
The path in the Image type indicates either the URL of the image or the filename for internal images (e.g. "image1.png").
{
"images": {
"image1": { "type": "file", "path": "image1.png" }
"image2": { "type": "url", "path": "https://www.website.com/image2.jpg" }
"image3": { "type": "file", "path": "image3.jpg" }
},
}
Property | Description |
---|---|
|
file or url |
|
string |
By default, geometry is rendered before sub-shapes, but this behavior can be overridden by setting the render order to subshape, which renders sub-shapes first, then geometry.
A rectangle styled with a red fill and a 3 pixel blue stroke and no rounding
{
style: {
fill: { type: 'color', color: '#ff0000' }
stroke: { color: '#0000ff', width: 3 }
rounding: 0
}
geometry: [
{ type: rect }
]
}
A rectangle with an image fill using an internal image file
{
...
images: {
"logo": {
type: 'file'
path: 'logo.png'
}
}
...
style: {
fill: {
type: 'image'
ref: 'logo'
mode: 'stretch'
}
stroke: { color: '#00000000', width: 1 }
rounding: 0
}
geometry: [
{ type: rect }
]
}
A rectangle styled with a transparent fill and a 1 pixel black stroke and 10px rounding
{
style: {
fill: { type: 'color', color: '#00000000' }
stroke: { color: '#000000', width: 1 }
rounding: 10
}
geometry: [
{ type: rect }
]
}
The top-level shape definition can provide geometry which is rendered, but oftentimes having smaller sub-shapes within the shape definition can simplify the definition and allow for some interesting shapes.
Sub-shapes have a similar definition to the shape definition, but allow for some additional functionality, such as condition rules, repeating rules and a configurable bounds.
If a sub-shape should only be shown under certain conditions, a condition can be defined which will only display the sub-shape(s) when the condition evaluates to true. This is useful to enable things like badges which are only displayed under certain conditions.
If the conditional expression is false, the sub-shape is not rendered at all.
Sub-shapes can be repeated in a similar way as geometry, using for or map repeats. For more information about the general repeat types, see Repeating Geometry.
Lucidchart will calculate the placement and size of a shape's geometry using the bounds of the shape (or sub-shape) definition that contains it. By default, the bounds of a parent shape will be applied to its sub-shapes, but you can use shape data or another formula to modify a sub-shape’s bounds and describe a new position or size for the sub-shape's geometry.
Bounds is defined by specifying four parts: Anchor Position, Anchor Offset, Size, and Rotation.
Property | Description |
---|---|
|
formula |
|
formula |
|
formula |
|
formula |
|
|
|
|
|
|
Property | Description |
---|---|
|
number |
|
number |
The anchor position of a shape’s bounds specifies the anchor/rotation point for the shape. Combined with the anchor offset, anchor position tells Lucidchart where to place the shape relative to its container. If an anchor position is not defined for the shape, Lucidchart will default to anchoring the shape to the top-left corner of the parent container.
Anchor position is defined using relative or absolute (x,y) coordinates. See the Absolute vs. Relative Coordinates section below to learn more.
The anchor offset of a shape’s bounds describes the location on the sub-shape that Lucidchart will use to position the shape. For example, an anchor offset of top-left will tell Lucidchart to position the sub-shape by placing its top-left corner at the anchor position.
Anchor offset can be specified directly as a relative coordinate within the sub-shape [from (0, 0) to (1, 1)] or can be specified using one of the predefined anchor types (defined below with each equivalent anchor coordinates).
As an illustration, in the example below there are 3 sub-shapes (A, B, and C), each with different anchor types and positions.
Examples of 3 sub-shapes with different anchor points: A, B, and C.
In the example, sub-shape A has an anchor offset of left, positioned at anchor point (0, 0.5). Sub-shape B has an anchor of center, and is positioned at (0.5, 0.5), which is the center of the container. Sub-shape C has an anchor of top-right, and is positioned at (1, 0), which is the top-right corner of the container.
Anchor Type | Description |
---|---|
top-left | Uses the top left corner of the sub-shape as the anchor point. Equivalent to an anchor of (0, 0). |
top | Uses the top of the shape vertically and the center of the shape horizontally as its anchor point. Equivalent to an anchor of (0.5, 0). |
top-right | Uses the top-right corner of the sub-shape as the anchor point (see sub-shape C in the above example). Equivalent to an anchor of (1, 0). |
left | Uses the left side of the sub-shape in the center vertically (see sub-shape A in the above example). Equivalent to an anchor of (0, 0.5). |
center | Uses the center of the sub-shape as its anchor (see sub-shape B in the above example). Equivalent to an anchor of (0.5, 0.5). |
right | Uses the right side of the sub-shape in the center vertically. Equivalent to an anchor of (1, 0.5). |
bottom-left | Uses an anchor in the bottom-left of the sub-shape. Equivalent to an anchor of (0, 1). |
bottom | Uses the bottom side of the sub-shape in the center horizontally. Equivalent to an anchor of (0.5, 1). |
bottom-right | Uses the bottom-right corner of the sub-shape as the anchor point. Equivalent to an anchor of (1, 1). |
The size of a shape’s geometry describes the width and height of the shape, specifying the extent of the shape’s bounds. Like anchor position, shape size can be specified using absolute or relative values. See the Absolute vs. Relative Coordinates section below to learn more.
A shape’s bounds can specify a rotation, which will be applied to shapes after they are rendered, using the anchor point as the point of rotation. The geometry itself operates purely on a non-rotated coordinate system for simplicity.
You can use absolute or relative coordinates to specify anchor position and shape size. Absolute coordinates are measured in pixels in screen space. When you use absolute coordinates to specify anchor position, you are telling Lucidchart to place the anchor point at an exact pixel location. Absolute coordinates are measured relative to the parent bounds; for example, if the absolute position (5, 5) was specified with an anchor of "top-left", the anchor point would be calculated as 5 pixels to the right and 5 pixels down from the parent's top-left coordinate.
While absolute coordinates refer to a specific pixel location, relative coordinates specify a percentage of the parent shape’s bounds (or shape bounds, if the sub-shape is a child of the shape definition). Relative coordinates are always in the range 0 to 1, with relative coordinates (0.5, 0.5) always positioning the shape in the center of its parent’s bounds. For example, if a shape’s container is 300 x 200 pixels and you specify a relative position of (0.1, 0.1), this will translate to absolute coordinates of (30, 20) in pixels. If the container is resized to 400 x 400 pixels, the relative position (0.1, 0.1) would become (40, 40).
Anchor positions are specified as x and y in the bounds, and are made absolute by including the characters "x" or "y", respectively, in the absolute string field. Width and height are specified as w and h, and are made absolute using 'w' or 'h', respectively, in the absolute string field.
Note: If bounds are not specified for a sub-shape, the sub-shape's bounds will default to the anchor point in the top left, and the bounds fills its container.
bounds: {
x: 5
y: 5
w: 5
h: 5
anchor: 'top-left'
// All coordinates are absolute.
// "absolute: true" is equivalent.
absolute: 'xywh'
}
bounds: {
x: 0.5
y: 0.5
w: 5
h: 5
anchor: 'center'
// Width and Height are absolute
absolute: 'wh'
}
bounds: {
x: 0,
y: 0.5,
w: 0.5,
h: 0.5,
anchor: 'left',
// None of the properties are absolute.
// - "absolute: ''" is equivalent.
// - No absolute field is also equivalent.
absolute: false
}
bounds: {
x: 1
y: 1
w: 25
h: 25
anchor: 'bottom-right'
absolute: 'wh'
}
bounds: {
x: 0.5
y: 0.5
w: 0.9
h: 0.9
anchor: 'center'
}
You can use formulas in your shape definition to tell Lucidchart to conditionally display and/or repeat a geometry or sub-shape description.
You can apply Lucidchart formulas to field values in your shape definitions to make the values change dynamically with data. Documentation for the Lucid formula system can be found online on Lucid's help site (How to use Formulas - Lucidchart) or in the internal Lucid formula function documentation (Internal Lucid Formula Functions Documentation).
In the custom shape format, formula expressions always start with "=", such as in the example: "=1 + 2". In the schema below, the data type formula specifies that a formula can be used.
You can add interpolated string values of the form "{{=
For example, when the string:
"Here is 1 + 2: {{=1 + 2}}"
is evaluated, it will produced the string:
"Here is 1 + 2: 3"
In the schema below, the data type interpolated specifies that an interpolated string can be used.
Shape data can be defined on the custom shape to allow users to configure the data used by the custom shape. Shape data can be used in any formula within the shape, using the syntax @PropertyName. For more information about Lucidchart's formula system, see the formulas page.
Property | Description |
---|---|
|
string The internal name of the field, used in formulas. |
|
string or TranslatableString The label displayed when the shape property is shown in the editor. |
|
The type of data this field stores. |
|
The default value for the field. This can be a formula or a constant value. |
|
Constraints which determine whether or not the property is valid. |
Property | Description |
---|---|
|
formula The condition checked for the constraint, which fails when this evaluates to false. |
|
The value used to resolve this constraint when it fails. |
|
The message shown to the user when the constraint fails. |
Property Type | Description | Example |
---|---|---|
boolean | True or false |
|
number | A number value, including both integers and decimal values |
|
string | A text value |
|
color | A color value, constructed using color functions or a hex color |
|
date | A date value, constructed using date functions |
|
picklist | A value selected from a list of possible values, displayed in the editor as a picklist. The value must be one of the available values in the picklist. | |
array | Multiple data values supplied as an array. Can be constructed using array functions |
|
object | A collection of key-value pairs. Can be constructed using object functions |
|
formula | Allows any formula value |
|
Shape data properties can have an optional list of constraints which restrict the values of data allowed. For example, a Percentage field could have a constraint where the value must be less than or equal to 100.
Constraints do not prevent saving shape data, but rather will indicate the value is invalid and allow an optional resolution value, which is used in place of the specified value. In the above example, if the Percentage field exceeds 100, the resolution value might be 100; if the value goes above 100, the resolution value (100) is used instead. If no resolution is provided, the shape will be shown as an error state.
Definitions can be used to simplify complex calculations by creating internal shape data that can be referenced much like shape data defined within Lucidchart. These definitions act like shape data properties that are not editable and are purely calculated using other values.
For example, if a shape data property named @Value was defined and calculations in geometry often need to use the square root of that value, a definition called @ValueSquareRoot could be created to simplify the formulas for the geometry (with the value defined as "=SQRT(@Value)"). This usage prevents the need to constantly reference "SQRT(@Value)" in formulas and allows the formula to be changed in the future in a single spot, instead of multiple places.
Definitions can be defined in the top-level of the shape definition itself and available to all geometry and sub-shape, or within a sub-shape which would be available to that sub-shape's geometry and sub-shapes.
Definitions must be defined with a data type, using one of the data types described in the Shape Data Properties section above.
As definitions are created, they are available for use within the shape they are defined or any sub-shapes below that shape. This is referred to as the scope of the variable, which describes where the variable may be used. The scope for sub-shapes in called a child scope or a lower scope, and the scope of the parent sub-shape (or top-level shape definition) is called a parent scope or a higher scope.
As an example, in the following figure there are 3 scopes (A, B, and C). A is the top-level scope and the parent scope of B. B is a child scope of A and a parent scope of C. Scope B is a higher scope than scope C, and a lower scope than scope A.
Example diagram showing 3 scopes
There are some exceptions to the general rule that definitions are available within the shape they are defined in for sub-shapes; this is due to the order in which formulas are evaluated when generating the geometry to render the shape definition.
Formulas in a shape definition are evaluated in the following order:
When using formulas in the custom shape format, there are some properties that are pre-filled with metadata about the shape. These can be used anywhere in the custom shape format and will always be available (unless overridden with a def of the same name).
Definition | Description |
---|---|
@Width | The width of the shape in pixels |
@Height | The height of the shape in pixels |
Text can be displayed on the shape using text areas. Text areas are defined in either the shape definition or in sub-shapes, which fill the bounds in which it is defined. Sub-shapes containing only text can be defined to specifically place text.
Text areas only define the placement and rendering of text; if a border is needed for a text area, for example, additional geometry must be used.
Property | Description |
---|---|
|
string |
|
string or formula |
|
|
|
|
|
|
|
|
|
|
Property | Description |
---|---|
|
|
|
|
|
|
|
|
Property | Description |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Text areas must have unique names, in order to allow text added to the shape (especially editable text) to be referenced outside of the shape and to ensure that each text area is uniquely defined.
Text areas use their own styling, independent of the shape's style, to define how the text is rendered. The following examples show properties that can be used to control text rendering:
TODO include styles table
In some cases, shape geometry may need to be clipped to fit to a specific outline. For example, a movie ranking shape (i.e. rating that uses stars) could be constructed using a rectangle shape that grows based on the value, which is then clipped to a shape consisting of the 5 stars.
Clipping for a shape is performed by taking all geometry created for the shape (both geometry in the shape and that shape's sub-shapes) and intersecting it with the clipping mask defined by the shape clipping settings.
Property | Description |
---|---|
|
Geometry[] |
|
|
Clipping removes all geometry that falls outside of the clipping path, but sometimes a geometry that equals the clipping path is needed. For example, in the star rating above, if we want a line to be placed around each star, we would need to add new geometry that matches the same path as the clip path. Instead, a stroke can be added to the clipping path which adds the extra geometry with the specific stroke settings without needing a separate copy of the geometry.
Property | Description |
---|---|
|
string or formula |
|
string or formula |
|
string or formula |
Shape constraints allow the shape to define limits that prevent the shape from going below a minimum size or above a maximum size. For both minimum and maximum size, both width and height are optional, so it is possible to have a shape that is limited in only one dimension in either direction or has different limits for height and width.
Shape constraints also allow locking controls on the shape. The shapes horizontal or vertical resizing can be limited to prevent the shape from being resized in that dimension.
Minimum size can be specified using a formula, which allows the minimum dimensions of the shape to be dynamically updated based on formula values. If resizing is also locked for that dimension, the shape's dimension will be fully dynamic.
Property | Description |
---|---|
|
A constraint that limits the minimum size of the shape. |
|
A constraint that limits the minimum size of the shape. |
|
options are 'horizontalResize' or 'verticalResize' |
Property | Description |
---|---|
|
|
|
|
Property | Description |
---|---|
|
If present, constrains the width to this number |
|
If present, constrains the height to this number |
{
properties: [
{
name: 'Min', label: 'Min', type: 'number', default: 1,
constraints: [{ condition: '=@Min < @Max', message: 'Min constraint' }]
}
{
name: 'Max', label: 'Max', type: 'number', default: 100,
constraints: [{condition: '=@Max > @Min', message: 'Max constraint' }]
}
{
name: 'Value', label: 'Value', type: 'number', default: 20,
constraints: [
{ condition: '=@Value >= @Min', resolution: '=@Min', message: 'Min value constraint' }
{ condition: '=@Value <= @Max', resolution: '=@Max', message: 'Max value constraint' }
]
}
{ name: 'Foreground', label: 'Foreground', type: 'color', default: '=fillColor()' },
{ name: 'Background', label: 'Background', type: 'color', default: '#D7E9FF' }
]
defs: [
{ name: 'Rounded', type: 'number', value: '=@Height / @Width' }
]
clip: {
geometry: [
{ type: 'union'
geometry: [
{ type: 'ellipse', w: '=@Rounded' }
{ type: 'rect', x: '=@Rounded / 2', w: '=1 - @Rounded' }
{ type: 'ellipse', x: '=1 - @Rounded', w: '=@Rounded' }
]
}
]
}
shapes: [
// Background
{
style: { fill: { type: 'color', color: '=@Background' } }
geometry: [{ type: 'rect' }]
}
// Progress
{
style: { fill: { type: 'color', color: '=@Foreground' } }
geometry: [
{ type:'rect', w: '=(@Value - @Min) / (@Max - @Min)' }
]
}
]
}
The rounded progress bar defines a shape which represents a progress bar with rounded edges showing a current value in a range defined by the user as shape data. This example uses clipping to ensure that the path doesn't warp when resized and to simplify the geometry used in the progress bar.
{
properties: [
{
name: 'Min',
label: 'Min',
type: 'number',
default: 0,
constraints: [{
condition: '=@Min < @Max',
message: 'Max must be larger than Min'
}]
},
{
name: 'Max',
label: 'Max',
type: 'number',
default: 100,
constraints: [{
condition: '=@Max > @Min',
message: 'Max must be larger than Min'
}]
},
{
name: 'Value',
label: 'Value',
type: 'number',
default: 70,
constraints: [
{
condition: '=@Value >= @Min',
resolution: '=@Min',
message: 'Value must be greater or equal to Min'
},
{
condition: '=@Value <= @Max',
resolution: '=@Max',
message: 'Value must be less or equal to Max'
}
],
},
{
name: 'Foreground',
label: 'Foreground',
type: 'color',
default: '=fillColor()'
},
{
name: 'Background',
label: 'Background',
type: 'color',
default: '#D7E9FF'
}
],
shapes: [
{
shapes: [
// Outer Path
{
style: {fill: {type: 'color', color: '=@Background'}}
geometry: [{type: 'rect'}]
}
// Inner Path
{
style: {fill: {type: 'color', color: '=@Foreground'}}
geometry: [{type: 'rect', w: '=(@Value - @Min) / (@Max - @Min)'}]
}
]
}
]
clip: {
geometry: [{
type: 'intersection',
geometry: [
// Triangle Path
{type: 'path', path: 'M 0,1 L 1,0 L 1,1 Z'}
{
type: 'union'
geometry: [
// Bars
{type: 'rect', x: 0, w: 0.233},
{type: 'rect', x: 0.256, w: 0.233},
{type: 'rect', x: 0.512, w: 0.233},
{type: 'rect', x: 0.767, w: 0.233},
]
}
]
}]
}
}
Shows a progress bar shape in the style of a signal strength meter. The existing shape is continuous, however the shape could be modified to round to the nearest 0.5 if needed.
{
properties: [
{ name: 'Min', label: 'Min', type: 'number', default: 0 }
{ name: 'Max', label: 'Max', type: 'number', default: 100 }
{ name: 'Value', label: 'Value', type: 'number', default: 20
constraints: [
{ condition: '=@Value >= @Min', resolution: '=@Min', message: 'Too low!' }
{ condition: '=@Value <= @Max', resolution: '=@Max', message: 'Too high!' }
]
}
{ name: 'Foreground', label: 'Foreground', type: 'color', default: '=fillColor()' }
{ name: 'Background', label: 'Background', type: 'color', default: '#D7E9FF' }
],
templates: [
{ name: 'stars'
geometry: [
{
type: union
geometry: [
{ type: 'polygon', n: 5, inset: 0.4, x: 0.0, y: 0, w: 0.2, h: 1 }
{ type: 'polygon', n: 5, inset: 0.4, x: 0.2, y: 0, w: 0.2, h: 1 }
{ type: 'polygon', n: 5, inset: 0.4, x: 0.4, y: 0, w: 0.2, h: 1 }
{ type: 'polygon', n: 5, inset: 0.4, x: 0.6, y: 0, w: 0.2, h: 1 }
{ type: 'polygon', n: 5, inset: 0.4, x: 0.8, y: 0, w: 0.2, h: 1 }
]
},
]
}
],
style: { fill: { type: 'color', color: '=@Background' } },
// Draw the background
geometry: [
{ type: 'template', template: 'stars' }
]
shapes: [
// Draw the foreground
{
style: { fill: { type: 'color', color: '=@Foreground' } },
geometry: [
{
type: intersection
geometry: [
{ type: 'template', template: 'stars' }
{ type: 'rect', w: '=(@Value - @Min) / (@Max - @Min)' }
]
}
],
},
// Outline the stars (using the foreground color darkened by 20%).
{
style: {
fill: { type: 'color', color: '#00000000' },
stroke: { color: '=DARKEN(@Foreground, 20%)', width: 1 },
},
geometry: [
{ type: 'template', template: 'stars' }
]
}
]
}
The star rating defines a shape which represents a progress bar as a star rating (in the vein of reviews). The progress bar is continuous and clips to the geometry of the stars. A discrete version could be implemented which rounds the input to the nearest 0.5 to make it map to whole or half stars.
{
properties: [
{
name: 'In',
label: 'In',
type: 'number',
default: 5,
constraints: [
{condition: '=@In > 0', resolution: 1, message: 'Must be greater than 0.'}
]
},
{
name: 'Out',
label: 'Out',
type: 'number',
default: 3,
constraints: [
{condition: '=@Out > 0', resolution: 1, message: 'Must be greater than 0.'}
]
},
],
style: {
fill: {type: 'color', color: '#f5f5f5'},
stroke: {color: '#999999', width: 2},
},
shapes: [
{ geometry: [{type: 'rect' }] }
{
repeat: {type: 'for', index: 'i', min: 1, max: '=@In', step: 1}
bounds: {x: '=@i / (@In + 1)', y: 0, w: '=1 / (@In + 1)', h: 0.2, anchor: 'top'}
style: {
fill: {type: 'color', color: '#cccccc'}
stroke: {color: '#999999', width: 2}
}
geometry: [{type: 'rect'}]
linkpoints: [ {x: 0.5, y: 0} ]
}
{
repeat: {type: 'for', index: 'i', min: 1, max: '=@Out', step: 1}
bounds: {x: '=@i / (@Out + 1)', y: 1, w: '=1 / (@Out + 1)', h: 0.2, anchor: 'bottom'}
style: {
fill: {type: 'color', color: '#cccccc'}
stroke: {color: '#999999', width: 2}
}
geometry: [{type: 'rect'}]
linkpoints: [ {x: 0.5, y: 1} ]
}
]
}
The multiplexer defines a shape that has a variable number of link points on the top and bottom, which are controlled by two shape data fields (In and Out). Link points are created using repeated shapes.
{
properties: [
{ name: 'PositiveColor', label: 'Positive Color', type: 'color', default: '#03adfc' },
{ name: 'NegativeColor', label: 'Negative Color', type: 'color', default: '#fc0362' },
{ name: 'Min', label: 'Minimum', type: 'number', default: -100 },
{ name: 'Max', label: 'Maximum', type: 'number', default: 100 },
{ name: 'Value', label: 'Value', type: 'number', default: 50 },
],
templates: [
{
name: 'bar'
geometry: [
{ type: 'rect', x: 0.5, w: '=@Value / (@Max - @Min)' },
],
}
],
shapes: [
{
style: {
fill: { type: 'color', color: '=fillColor()' }
stroke: { color: '=DARKEN(fillColor(), 20%)', width: 1 },
},
geometry: [
{ type: 'rect' },
{ type: 'path', path: 'M 0.5 0 L 0.5 1'}
],
}
{
style: {
fill: { type: 'color', color: '=IF(@Value < 0, @NegativeColor, @PositiveColor)' }
},
geometry: [
{
type: 'template'
template: 'bar'
},
],
},
],
}
{
"properties": [
{ "name": "Name", "label": "Name", type: "string", "default": "You" }
],
"style": {
"fill": { "type": "color", "color": "=fillColor" },
"stroke": { "color": "#000000", "width": 2 }
},
"shapes": [
{
"geometry": [ { "type": "rect" } ],
"textarea": {
"name": "t0",
"text": "Hello, {{=@Name}}!",
"editable": true,
"style": { "color": "#000000" }
}
}
]
}
The greeting shape is a simple example of a textarea on a shape. The shape is a simple rectangle with a fill of the current fill color, a black border, and a textarea that fills the shape.
Term | Definition |
---|---|
LCSZ | The file format this document describes. LCSZ is intended to mean Lucid Custom Shape ZIP. |
Shape Library | A collection of shape definitions, images, text translations and shape library entries stored in a ZIP file format that can be uploaded to Lucidchart as a new shape library. |
Shape Library Entry | A shape shown to a user in the list of shapes in a shape library (in Lucidchart). Each shape library entry is composed of a shape definition and a list of settings for that shape library entry; each entry can be a unique set of default properties using a single shape definition. |
Shape Definition | A file that defines the structure of a shape, properties associated to that shape and interactions. A shape definition specifies how shapes are drawn; a shape entry matches up a shape definition with it's default properties and settings. |
Shape | An entity which includes scoped definitions, geometries for rendering, and a group of subshapes. Rendering a shape renders both geometries and subshapes; the order field defines whether geometries or shapes are rendered on top or not. |
Sub-Shape | A shape contained within another shape, which can have its own render style, bounds and definitions. Sub-shapes can be contained within the shape itself, or within another sub-shape. |
Geometry | A path defined within a shape. Helpers like rect and ellipse give the user easier tools to add standard geometry to a shape. |
Top-Level Geometry | The geometry defined on shape definition itself and not in sub-shapes. |
Template | A collection of geometry that can be reused multiple times using a set of passed in parameters. |
i18n | Internationalization. Provides translations for text labels used by shapes and the shape library. Not yet implemented. |
Clipping Mask | A clipping mask defines the bounds of the shape; no geometry should be present outside of the clipping mask. At a technical level, all generated shape geometry is intersected with the clipping mask to produce the final geometry. |
Link point | A connection point that is shown when users are dragging a line to connect with the shape to allow the line to snap to a specific point. |
Textarea | A block of optionally editable text displayed for a shape. Textareas are positioned relative to the current shapes offset. |