NAV

Introduction

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.

Known Issues

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:

  • User-defined i18n translations are parsed and verified, but not currently used.
  • Formatting inside of text areas is not implemented (textareas as a whole can be formatted, but the format cannot change within the textarea).
    • In the future, dynamic textareas that aren't backed by block properties will allow this, but only for non-editable text areas.

Getting Started

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.

HJSON

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):

{"a": b}

For more information about the HJSON format, see https://hjson.org/.

Getting started with the LCSZ Editor

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.

Loading and Saving

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.

Getting started with the Extension API

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 library.manifest.

For creating a library just from images see create-image-shape-library.

Testing

Any time you are running npx lucid-package test-editor-extension, all shape libraries in the package will be automatically displayed in the Lucidchart toolbox. You can also use npx lucid-package test-shape-libraries to test your shape libraries without needing any editor extension to exist.

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.

File organization and library.manifest

In the new shape library directory there will be a folder images where you can store image files to be referenced in your shapes. In shapes there will be any number of .shape files which will be referenced in the manifest. These shape files define the rendering of shapes and will be covered in more detail below.

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.

Library manifest schema
Default library.manifest
{ "name": "Test Library", "shapes": [ { "shape": "first", "name": "Test Shape", "defaults": { "fillColor": "#ff0000", "strokeColor": "#00ffff", "strokeWidth": 3, "width": 300, "height": 300 } } ] }
Property Description
name string or TranslatableString
The name of the shape library
shapes ShapeEntry[]
The list of shapes in this shape library. Only shapes in this list are shown
ShapeEntry
Property Description
shape string
The basename for the shape file, found in /shapes/shapeId.shape
key string (optional)
If specified, can be used to differentiate between multiple shape entries with the same shape value when calling EditorClient.getCustomShapeDefinition
name string or TranslatableString
The name displayed for this shape in the shape library
defaults ShapeDefaults
Default block properties, like width, height, fill, etc.
properties {string:string}
Default values for custom shape data, defined in the shape definition
ShapeDefaults
Property Description
width number
The initial shape width
height number
The initial shape height
rounding number
The initial shape rounding
fillColor number
The initial fill color for the shape
strokeColor number
The initial line color for the shape
strokeWidth number
The initial line width for the shape
opacity number
The initial opacity for the shape
rotation number
The initial rotation for the shape
aspectRatio number
fixed aspect ratio. See also locked
link string
Starting url link on the shape

TranslatableString

Property Description
default string
The fallback value to display if the i18n value is not found
i18n string
The i18n key from the i18n files

Create custom shapes on-canvas

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); } } });

Custom Shape Library

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 .shape files under /shapes when creating libraries with the extension API. In the online editor each Defintion you modify will have the exact same contents as a .shape file.

Manifest file

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.

Full manifest
{ "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 File

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.

Example image shape file
{ "locked": ["aspectRatio"], "images": { "imageReference": { "path": "file", "type": "file", }, }, "geometry": [ { "type": "rect", }, ], "style": { "fill": { "type": "image", "ref": "imageReference", "mode": "stretch", }, }, }

Geometry

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).

Conditional 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).

Geometry Schema

All geometry defined in the custom shape format follows the general schema:

Geometry Example
{ "type": "rect", "repeat": { "min": 5, "max": 20 }, "localFill": false }
Property Description
type string
The type of geometry
Options: rect, ellipse, polygon, path, union, intersection, difference, xor
condition formula
A formula which controls whether or not the geometry is included in the output
repeat ForRepeat or MapRepeat
localFill boolean or formula
Defines whether or not this geometry starts a gradient
defs Definition[]
Allows local definition of formula (see Definitions below)

Definition

Property Description
name string
value formula

Repeating Geometry

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.

ForRepeat

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
index string
min number or formula
max number or formula
step number or formula
MapRepeat

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
index string
min number or formula
max number or formula
step number or formula

Rectangle

Property Description
type 'rect'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Rect example
{ type: "rect" // NOTE: Unspecified x and y default to 0. // NOTE: Unspecified w and h default to 1. }

Rect

Rect example
{ type: "rect", x: 0.5, y: 0.5, w: 0.5, h: 0.5 }

Rect

Ellipse

Property Description
type 'ellipse'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Ellipse example
{ type: "ellipse" }

Ellipse

Polygon

Property Description
type 'polygon'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
n number or formula
The number of sides of this polygon
x number or formula
The x-coordinate of this rectangle, relative to its bounds (default: 0)
y number or formula
The y-coordinate of this rectangle, relative to its bounds (default: 0)
w number or formula
The width of this rectangle, relative to its bounds (default: 1)
h number or formula
The height of this rectangle, relative to its bounds (default: 1)
Polygon example
{ type: "polygon" n: 6 }

Polygon

Polygon example
{ type: "polygon", n: 5, inset: 0.5, w: 0.5, h: 0.5 }

Star

Path

Property Description
type 'path'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
path interpolated string
The defined path of the Path geometry. Uses SVG paths and assumes relative coordinates.
Path example
{ type: "path", path: "M 0 0 L 1 1 L 0 1 Z" }

Path

You can learn more about SVG paths online

Boolean Operation

Boolean geometry operations require the geometry it operates on to be defined:

Property Description
type 'union', 'intersection', 'difference', 'xor'
condition formula
repeat repeat
step boolean or formula
defs Definition[]
geometry Geometry[]
The geometry used in the Boolean operation
Union example
{ 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 } ] }

Union




Intersection example
{ 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 } ] }

Intersection




Difference example
{ 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 } ] }

Difference




XOR example
{ 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 } ] }

XOR

Render Style

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.

Formatting

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.

Image Map

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 images: { string: Image }

The path in the Image type indicates either the URL of the image or the filename for internal images (e.g. "image1.png").

Note: SVG files are supported, however, these vector images will be rasterized.

Image Schema
An example of image map entries
{ "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
type file or url
path string

Note: External URLs (i.e. https://www.fakeurl.com/image2.jpg, above) directly reference URLs and do not add images to the Lucidchart image manager. URLs that produce images like single-pixel image trackers can be used for tracking shape usage (as it will operate as a normal web-based image), however if the URL stops being valid or the image referenced by the URL changes, that will affect the shape in the future.

Render Order

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.

Examples

A rectangle styled with a red fill and a 3 pixel blue stroke and no rounding

Example
{ style: { fill: { type: 'color', color: '#ff0000' } stroke: { color: '#0000ff', width: 3 } rounding: 0 } geometry: [ { type: rect } ] }

Border

A rectangle with an image fill using an internal image file

Example
{ ... 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 } ] }

Border

A rectangle styled with a transparent fill and a 1 pixel black stroke and 10px rounding

Example
{ style: { fill: { type: 'color', color: '#00000000' } stroke: { color: '#000000', width: 1 } rounding: 10 } geometry: [ { type: rect } ] }

Border

Sub-Shapes

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.

Conditional Shapes

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.

Repeating Shapes

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.

Bounds

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.

ShapeBounds
Property Description
x formula
y formula
w formula
h formula
absolute boolean or string
rotation formula
anchor string or Anchor
Anchor
Property Description
x number
y number

Anchor Position

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.

Anchor Offset

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.

Anchor Points

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).

Size

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.

Rotation

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.

Rotation

Absolute vs. Relative Coordinates

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.

Example bounds

Top Left
bounds: { x: 5 y: 5 w: 5 h: 5 anchor: 'top-left' // All coordinates are absolute. // "absolute: true" is equivalent. absolute: 'xywh' }

Top Left

Center
bounds: { x: 0.5 y: 0.5 w: 5 h: 5 anchor: 'center' // Width and Height are absolute absolute: 'wh' }

Center

Left
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 }

Left

Bottom Right
bounds: { x: 1 y: 1 w: 25 h: 25 anchor: 'bottom-right' absolute: 'wh' }

Bottom Right

Center
bounds: { x: 0.5 y: 0.5 w: 0.9 h: 0.9 anchor: 'center' }

Center

Formulas

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.

Interpolated Strings

You can add interpolated string values of the form "{{=}}" to some field values, which act like inline formulas. Lucidchart will replace these strings with the formula's calculated value when building the string. Interpolated strings with no interpolated values will be treated as normal string field values.

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 Properties

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.

ShapeProperty schema

Property Description
name string
The internal name of the field, used in formulas.
label string or TranslatableString
The label displayed when the shape property is shown in the editor.
type number, string, color, date, boolean, array, object, formula
The type of data this field stores.
default boolean, number, string, formula
The default value for the field. This can be a formula or a constant value.
constraints [Constraint[]](#constraint-schema
Constraints which determine whether or not the property is valid.

Constraint Schema

Property Description
condition formula
The condition checked for the constraint, which fails when this evaluates to false.
resolution number, string, formula
The value used to resolve this constraint when it fails.
message string or TranslatableString
The message shown to the user when the constraint fails.

Types

Property Type Description Example
boolean True or false true
false
number A number value, including both integers and decimal values 5
-3
3.14
string A text value "ABC"
"123-456-7890"
color A color value, constructed using color functions or a hex color "=RGB(255, 0, 0)"
"#ff0000"
date A date value, constructed using date functions "=DATE(2019, 1, 1)"
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 "=ARRAY(1, 2, 3)"
object A collection of key-value pairs. Can be constructed using object functions '=OBJECT("A", 1, "B", 2)'
formula Allows any formula value "=1+2"

Constraints

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

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.

Types

Definitions must be defined with a data type, using one of the data types described in the Shape Data Properties section above.

Scope and Evaluation Order

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.

Scopes

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:

  • Conditions - The sub-shapes condition is evaluated first and do not use the local definitions
  • Repeat - Repeat fields do not use the local definitions. The repeat definitions (for index / value) are evaluated and added to a new local scope.
  • Definitions - Definitions are evaluated and added to the current local scope.
  • Bounds - Bounds are evaluated, using both repeat and definitions
  • Text Areas - Text areas are evaluated, using both repeat and definitions
  • Link Points - Link points are evaluated, using both repeat and definitions
  • Geometry - Geometry are evaluated, using both repeat and definitions
  • Sub-Shapes - Sub-shapes are evaluated using the same evaluation order (with the current scope becoming the new parent scope).

Built-In Definitions

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

Textareas

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.

Textarea Schema

Property Description
name string
text string or formula
margins number or TextMargins
editable boolean
style TextStyle
align string
valign string

TextMargins Schema

Property Description
top number
right number
bottom number
left number

TextStyle Schema

Property Description
color string
size number
bold boolean
underline boolean
italic boolean
font string
strikethrough boolean

Name

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.

Style

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

Clipping

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.

Clipping Schema

Property Description
geometry Geometry[]
stroke Stroke

Stroke

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
color string or formula
pattern string or formula
width string or formula

Shape Constraints

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.

ShapeDefinition schema

Property Description
minSize ConstraintSizeFormula
A constraint that limits the minimum size of the shape.
maxSize ConstraintSizeFormula
A constraint that limits the minimum size of the shape.
locked string[]
options are 'horizontalResize' or 'verticalResize'

ConstraintSizeFormula schema

Property Description
width number or formula
height number or formula

ConstraintSize schema

Property Description
width number
If present, constrains the width to this number
height number
If present, constrains the height to this number

Example Shapes

Rounded Progress Bar

Rounded Progress Bar
{ 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.

image

Signal Strength

Signal Strength
{ 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.

image

Star Rating

Star Rating
{ 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.

image

Multiplexer Shape

Multiplexer Shape
{ 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.

image

Center-Growing Progress Bar

Center-Growing Progress Bar
{ 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' }, ], }, ], }

image

Greeting Shape

Greeting Shape
{ "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.

image

Appendices

Definitions

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.