Introduction
Comb Script is a language for describing technical vector designs and a tool that exports these designs as SVG files. A primary goal of Comb Script is to express designs naturally, so they are easier to adjust and customize.
Comb Script was inspired by OpenSCAD, CSS, HTML, and Adobe Illustrator snapping shenanigans.
Parametric
Because shapes in Comb Script can be positioned relative to the bounds of their parents, it is possible to make designs that flex when dimensions change.
Expressive
Positions can be described in multiple ways allowing more natural expression. For example you can specify that a circle is 10 units from the right side of its container and vertically centered, rather than expressing its global coordinates.
Declarative
Comb Script is more like HTML than JavaScript. Ideally, your document describes your design, not the steps required to make it.
Unstyled
Shapes in Comb Script represent only the path data and don't have their own style properties like fill-color, stroke-color, or stroke-width. Comb Script is primarily designed for designs that describe paths for laser-cutters and plotters where such styles are not needed.
Boolean Operations
Combine shapes using boolean operations: Intersection, Union, and Difference
Render to SVG
Shape data can be exported as SVG using style templates.
Basic Syntax
Comb Script documents are written in YAML, a common and human-readable data-file format. Comb Script recognizes predefined keywords to describe regions, shapes, and their properties.
Here is an example of a simple Comb Script document that draws an ellipse:
properties:
width: 525
height: 225
children:
- ellipse:
properties:
width: 200
height: 200
This example begins by defining two document properties, width and height that describe the dimensions of the document. If these properties are not defined, defaults will be used. Document properties are defined using the properties: key followed by a set of indented key: value pairs.
Next, the example defines its children under the children: key. The children are indented one level and each is prefixed with a - to indicate a YAML array item. There are several types of children in Comb Script including regions, rectangles, and ellipses.
This document has one child, an ellipse. The ellipse defines width and height properties to set its dimensions.
Regions
The basic building-blocks in Comb Script are called regions. A region describes a rectangular bounds in the drawing and and can contain child regions. Child regions are laid out in relation to the bounds of their parent.
There are several types of regions in Comb Script. The basic region type is primarily organizational. It is used to define a new part of the drawing and to group its children. The basic region will not generate a vector shape in the exported SVG.
The rectangle or ellipse region types have all the same properties of the basic region but also draw a shape in the export.
Region Bounds
Every region has a set of bounds that describe a rectangular area in the drawing. These bounds can be set using the top, left, bottom, and right properties. These values are relative to the registration position of the region's parent.
A region's bounds can also be specified relative to the parent's bounds using margin_top, margin_left, margin_bottom, and margin_right properties.
You can specify the dimensions of the bounds with the width and height properties.
These properties can be mixed any way you wish, as long as they don't conflict.
children:
- region:
properties:
top: -100
bottom: 0
margin_left: 10
width: 100
Registration
Regions can optionally set their registration position with the registration property. The new registration position will be used by the region's children when laying out their bounds. It will also be used for the pivot point for rotations and scales applied to the region.
The default value of registration is parent which will keep the registration position set by the region's parent. Other values, such as 'top_left' and 'center' move it to a position determined by the region's bounds.
children:
- region:
properties:
width: 200
height: 200
registration: top_right
children:
- rectangle:
properties:
top: 0
left: 0
width: 75
height: 75
Transformations
You can transform the coordinate system used by a region and its children using the rotation, scale_x, and scale_y properties.
children:
- rectangle:
properties:
width: 100
height: 100
rotation: 45
Children
Regions can have any number of child regions. A region's bounds, registration, and transform are used by when positioning its children. Using nesting, you can describe the positional relationships of your shapes making parts of your design parametric.
In the following example the position of the two ellipses is determined by the width of their parent.
children:
- region:
properties:
width: 400
height: 200
children:
- ellipse:
properties:
height: 100
width: 100
margin_left: 30
- ellipse:
properties:
height: 100
width: 100
margin_right: 30
Shapes
The rectangle and ellipse region-types will generate a shape in the export. In these examples, exported shapes are filled in.
children:
- rectangle:
properties:
margin_left: 10
width: 200
height: 200
radius: 10
- ellipse:
properties:
margin_right: 10
width: 200
height: 200
radius: 20
Combining Shapes
Shapes can be combined using the boolean property. The possible values are add, subtract, and intersect.
Shapes with a set boolean property are combined with their parent's shape. If the parent does not generate a shape the shape of the first child will be used.
children:
- rectangle:
properties:
name: base_rectangle
width: 100
height: 100
children:
- ellipse:
properties:
name: subtract_ellipse
height: 100
width: 100
left: -100
top: -100
boolean: subtract
children:
- region:
properties:
name: parent_region
width: 300
height: 200
children:
- ellipse:
properties:
margin_left: 0
height: 200
width: 200
- ellipse:
properties:
margin_right: 0
height: 200
width: 200
boolean: intersect
You can use booelan operations will applied to a region's children as well. For example you can subtract many shapes from a single shape, subtract a single shape from many shapes, or even subtract many shapes from many shapes.
children:
- region:
properties:
width: 100
height: 200
children:
- ellipse:
properties:
margin_top: 0
height: 85
- ellipse:
properties:
margin_bottom: 0
height: 85
- region:
properties:
width: 200
height: 100
boolean: subtract
children:
- ellipse:
properties:
margin_left: 0
width: 85
- ellipse:
properties:
margin_right: 0
width: 85
Grids
Comb Script provides a special region-type called a region_grid. Region grids can be used to draw a region repeatedly in a rectangular grid.
The rows and columns properties will set the number of rows and columns in the grid directly. You can set row_height and column_width instead to create as many rows or columns as needed to fill the region grid.
Each cell in the generated grid will contain a clone of the children of the region grid.
children:
- region_grid:
properties:
width: 400
height: 200
column_width: 50
rows: 4
registration: center
children:
- ellipse:
properties:
width: 30
height: 30
YAML Repeated Nodes
YAML's repeated node feature can be used to to create named parameters and library shapes that can be used throughout your document. Repeated nodes are defined with & followed by a name, and used with * followed by the name. Repeated nodes are genrally defined as an array under the library: key.
Constants
You can use repeated nodes as named constants (aka variables or parameters) to identify commonly used values in your document and create a single place in the document where they can be changed.
library:
- &size 200
children:
- ellipse:
properties:
width: *size
height: *size
Library Items
It is common that you will reuse the same shape in multiple places in your document. Repeated nodes can represent entire regions, their properties, and even nested children. Each repeated node will be separately laid out in their parent's context.
library:
- &ex
rectangle:
properties:
margin_top: 10
margin_bottom: 10
width: 40
radius: 4
children:
- region:
properties:
margin_left: 10
width: 150
height: 150
registration: center
children:
- *ex
- region:
properties:
margin_right: 10
width: 100
height: 100
registration: center
children:
- *ex
{{class}}
-
{{#keyword}}
- keyword:
- {{keyword}} {{/keyword}} {{#extends}}
- extends:
- {{extends}} {{/extends}}
Required Properties
-
{{#required_properties}}
- {{>prop}} {{/required_properties}}
Properties
-
{{#properties}}
- {{>prop}} {{/properties}}
Inherited Properties
-
{{#inherited_properties}}
- {{>prop}} {{/inherited_properties}}
- type:
- {{type}}
- default:
- {{default}}{{^default}}undefined{{/default}} {{#required}}
- required:
- {{required}} {{/required}} {{#values.length}}
- values: {{#values}}
- {{.}} {{/values}} {{/values.length}}