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.

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
{{#regionTypes}}

{{class}}

{{#keyword}}
keyword:
{{keyword}}
{{/keyword}} {{#extends}}
extends:
{{extends}}
{{/extends}}
{{{description}}}
{{#required_properties.length}}

Required Properties

    {{#required_properties}}
  • {{>prop}}
  • {{/required_properties}}
{{/required_properties.length}} {{#properties.length}}

Properties

    {{#properties}}
  • {{>prop}}
  • {{/properties}}
{{/properties.length}} {{#inherited_properties.length}}

Inherited Properties

    {{#inherited_properties}}
  • {{>prop}}
  • {{/inherited_properties}}
{{/inherited_properties.length}}
{{/regionTypes}}