Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System Functions Reference

System functions are pre-defined arrow functions for common CAM outline shapes. They are automatically loaded into every FSL program.

Usage

Call a system function like any other arrow function:

gear = 96
Round()                    // Uses defaults: angle=43, sym=8
Round(45, 6)               // Override parameters
Oval(45, 1.5)              // Override length-width ratio (angle defaults to 45)

Available Functions

FunctionParametersDescription
Roundangle=43, sym=8Simple round preform
Ovalangle=45, lwr=1.2, segmentsPerQuad=5Elliptical outline
Rectlwr=1.5, angle=45Rectangular outline
RectTrlwr=1.5, angle=45, truncation=0.3, offset=0Rectangle with truncated corners
RectDblTrlwr=1.5, angle=45, first_truncation=0.3, second_truncation=0.2, offset=0Double-truncated rectangle for octagons
SquareTrangle=42, truncation=0.3Square with truncated corners
SquareCushTrcushion=1, angle=45, truncation=0.4Cushion square with truncation
PentCushTroffset=1, angle=45, truncation=0.3Pentagon cushion with truncation
Shieldangle=45Shield outline (threefold symmetry)
TriTrtruncation=0.25, angle=42Triangle with truncated corners
TriCushTrtruncation=0.25, angle=42, cushion=1Cushion triangle
TriCurvedangle=45, c1=2, c2=4Curved trilliant

Source Code

The system functions are defined using FSL. Here is the complete source:

// System library - lambda syntax with defaults

Round = (angle = 43,  sym = 8) => {

    assert(gear%sym == 0,  "Gear must be divisible by symmetry count")
    G1 0 @ 90 : size x sym
    P1 0 @ angle : cp() x sym
}

Rect = (lwr = 1.5,  angle = 45) => {

    assert(gear%4 == 0,  "Gear must be divisible by 4 for Rect")

    beta = Math.toDegrees(Math.atan(Math.tan(Math.toRadians(angle)) / lwr))
    corner_index = gear / 4
    PF1 corner_index @ beta : cp() xx2
    PF2 0 @ angle : cp() xx2
    G1 corner_index @ 90 : 0.5 xx2
    G2 0 @ 90 : mp(G1,  PF1,  PF2) xx2
}

RectTr = (lwr = 1.5,  angle = 45,  truncation = 0.3,  offset = 0) => {

    assert(gear%8 == 0,  "Gear must be divisible by 8 for RectTr")
    beta = Math.toDegrees(Math.atan(Math.tan(Math.toRadians(angle)) / lwr))
    corner_index = gear / 4
    PF1 corner_index @ beta : cp() xx2
    PF2 0 @ angle : cp() xx2
    G1 corner_index @ 90 : 0.5 xx2
    G2 0 @ 90 : mp(G1,  PF1,  PF2) xx2

    tr_index = gear / 8 + offset
    PF3 tr_index : cp() : ep(edge(PF1 : corner_index,  G1 : corner_index),  1 - truncation / 2) xx2
    G3 tr_index @ 90 : mp(G2,  PF3,  PF2) xx2
}

RectDblTr = (lwr = 1.5,  angle = 45,  first_truncation = 0.3,  second_truncation = 0.2,  offset = 0) => {

    // Double-truncated rectangle for octagons.

    assert(lwr > 0,  "Length-to-width ratio must be positive.")
    assert(gear%4 == 0,  "Gear index should be a multiple of 4.")
    assert(first_truncation >= 0,  "First truncation must be non-negative.")
    assert(first_truncation <= 1,  "First truncation should stay within 0-1.")
    assert(second_truncation >= 0,  "Second truncation must be non-negative.")
    assert(second_truncation <= 2,  "Second truncation should stay within 0-2.")
    corner_index = gear / 4
    split_index = Math.floor(gear / 8) + offset
    outer_ratio = first_truncation / 2
    inner_ratio = second_truncation / 2
    yo = corner_index + offset
    PF1 yo @ Math.toDegrees(Math.atan(
    Math.tan(Math.toRadians(angle)) / lwr)) : cp() x 2
    PF2 offset @ angle : cp() x 2
    G1 yo @ 90 : size x 2
    G2 offset @ 90 : mp(G1,  PF1,  PF2) x 2
    PF3 split_index
    : cp()
    : ep(edge(PF1 : (corner_index + offset),  G1 : (corner_index + offset)),  1 - outer_ratio) xx2
    G3 split_index @ 90 : mp(G2,  PF3,  PF2)
    yoyo = (Math.floor(3 * gear / 16) + offset)
    PF4 yoyo
    : cp()
    : ep(edge(PF3 : split_index,  G3 : split_index),  inner_ratio) xx2
    yoyo7 = (Math.floor(gear / 16) + offset)
    PF5 yoyo7
    : cp()
    : ep(edge(PF3 : split_index,  G3 : split_index),  1 - inner_ratio) xx2
    yo2 = (Math.floor(gear / 16) + offset)
    G4 yo2 @ 90 : mp(G3,  PF5,  PF3) xx2
    yo3 = (Math.floor(3 * gear / 16) + offset)
    G5 yo3 @ 90 : mp(G3,  PF4,  PF3) xx2
}

SquareTr = (angle = 42,  truncation = 0.3) => {

    assert(gear%8 == 0,  "Gear must be divisible by 8 for SquareTr")
    corner_index = gear / 8
    PF1 0 @ angle : cp() x4
    G1 0 @ 90 : size x4
    PF2 corner_index : cp() : ep(edge(PF1 : 0,  G1 : 0),  1 - truncation / 2) x4
    G2 corner_index @ 90 : mp(G1,  PF1,  PF2) x4
}

Shield = (angle = 45) => {

    assert(gear == 96,  "Shield macro requires 96 index gear")
    PF1 6 @ angle : cp() xx3
    G1 6 @ 90 : size xx3
}

TriTr = (truncation = 0.25,  angle = 42) => {

    assert(gear%6 == 0,  "Gear must be divisible by 6 for TriTr")
    PF1 0 @ angle : cp() x3
    G1 0 @ 90 : 0.7 x3
    PF2 gear / 6 : cp() : ep(edge(PF1 : 0,  G1 : 0),  1 - truncation / 2) x3
    G2 gear / 6 @ 90 : mp(G1,  PF1,  PF2) x3
}

Oval = (angle = 45,  lwr = 1.2,  segmentsPerQuad = 5) => {

    assert(lwr > 0,  "Length-to-width ratio must be positive")
    assert(segmentsPerQuad >= 2,  "segmentsPerQuad must be at least 2")
    assert(gear%4 == 0,  "Gear index should be a multiple of 4")

    width = 1
    length = lwr
    rx = length / 2
    ry = width / 2
    quarter = gear / 4
    steps = segmentsPerQuad
    stepSize = quarter / steps
    h = Math.cos(angle * Math.PI / 90)

    Math.range(1,  steps + 1).forEach(k => {

        idx = Math.round(k * stepSize)%gear
        ang = (idx / gear) * 2 * Math.PI
        c = Math.cos(ang)
        s = Math.sin(ang)
        d = Math.sqrt(rx * rx * c * c + ry * ry * s * s)

        prevK = k - 1
        prevIdx = Math.round(prevK * stepSize)%gear

        prevAng = (prevIdx / gear) * 2 * Math.PI
        prevC = Math.cos(prevAng)
        prevS = Math.sin(prevAng)
        prevD = Math.sqrt(rx * rx * prevC * prevC + ry * ry * prevS * prevS)

        det = prevC * s - prevS * c
        x = (prevD * s - d * prevS) / det
        y = (d * prevC - prevD * c) / det

        concat(O,  k) prevIdx : Point(x,  y,  - h) : cp() xx2

        k == steps ? {

            concat(O,  k + 1) quarter : Point(x,  y,  - h) : cp() xx2
        }
        : 0
    }
    )

    G1 0 @ 90 : size

    Math.range(2,  steps + 1).forEach(k => {

        idx = Math.round((k - 1) * stepSize)%gear
        concat(G,  k) idx @ 90 : mp(concat(G,  k - 1),  concat(O,  k - 1))
    }
    )
    concat(G,  steps + 1) quarter @ 90 : mp(concat(G,  steps),  concat(O,  steps))
}

SquareCushTr = (cushion = 1,  angle = 45,  truncation = 0.4) => {

    assert(cushion >= 1,  "Index offset must be 1 or more")
    assert(gear%4 == 0,  "Gear must be multiple of 4")
    side_index = cushion
    corner_index = gear / 8

    G1 side_index @ 90 : size xx4
    PF1 side_index @ angle : cp() xx4

    PF2 corner_index : cp() : ep(edge(PF1 : side_index,  G1 : side_index),  truncation / 2) x4
    G2 corner_index @ 90 : mp(G1,  PF2,  PF1) x4
}

PentCushTr = (offset = 1,  angle = 45,  truncation = 0.3) => {

    assert(offset >= 1,  "Offset >= 1")
    assert(truncation >= 0)
    assert(truncation <= 1)
    assert(gear%10 == 0)

    side_index = offset
    corner_index = gear / 10

    G1 side_index @ 90 : size xx5
    PF1 side_index @ angle : cp() xx5

    PF2 corner_index : cp() : ep(edge(PF1 : side_index,  G1 : side_index),  truncation) xx5
    G2 corner_index @ 90 : mp(G1,  PF1,  PF2) xx5
}

TriCushTr = (truncation = 0.25,  angle = 42,  cushion = 1) => {

    assert(truncation >= 0)
    assert(truncation < 2)
    assert(cushion >= 0)
    assert(gear%6 == 0)

    PF1 cushion @ angle : cp() xx3
    G1 cushion @ 90 : 0.7 xx3

    PF2 gear / 6 : cp() : ep(edge(PF1 : cushion,  G1 : cushion),  truncation) x3
    G2 gear / 6 @ 90 : mp(G1,  PF1,  PF2) x3
}

TriCurved = (angle = 45,  c1 = 2,  c2 = 4) => {

    cube = 3.4
    assert(c1 < c2)
    assert(gear%c1 == 0)
    assert(gear%c2 == 0)

    PF1 c1 @ angle : cp() xx3
    G1 c1 @ 90 : size
    PF2 c2 : cp() : ep(edge(PF1 : c1,  G1 : c1),  0.5) xx3
    G2 c2 @ 90 : mp(PF1,  PF2,  G1) xx3
}