FCS Essentials
FCS is a declarative language used to describe geometry, parameters, materials, loads, and generated outputs in HiStruct workflows. Most files use the .fcs extension.
Core Concepts
- Declarative: you describe what the model is, not a step-by-step execution sequence.
- Lazy evaluation: expressions are re-evaluated when dependencies change.
- Expression-oriented: every expression returns a value, so values can be composed freely.
- Domain keywords: the language includes built-in concepts for geometry, mesh, materials, layers, loads, and coordinate systems.
- File format: FCS source files use the
.fcsfile extension.
Practical tip: think of an FCS file as a live model graph. When one value changes, dependent expressions update automatically.
Variables
Top-level declarations use :=.
# Lazy binding (re-evaluates when dependencies change)
x := 10
name := "hello"
area := width * height # computed — changes when width or height change
# Inside parameter blocks and object literals, use =
myObj := MyClass{ param1 = 10, param2 = "test" }
:= vs =
This distinction is one of the most important FCS habits to learn.
:=— use for top-level variable declarations and lazy bindings=— use inside parameter blocks,{}clauses, and object literals- Using the wrong one may silently create unexpected behavior
Correct usage
width := 10
height := 8
area := width * height
panel := RectPanel{
Width = width
Height = height
Label = "A1"
}
Why it matters
At the top level, := creates a model binding. Inside object-style configuration blocks, = assigns parameter values immediately within that object literal or clause.
Common mistake
# Good
beamLength := 12
# Good
member := Beam{ Length = beamLength }
# Bad: using = at top level can behave differently than intended
beamLength = 12
Gotcha: if a value belongs to a
parameters { ... }block, object literal, or inline config block, use=there even if the surrounding file mostly uses:=.
Types
FCS commonly uses the following value categories.
Numbers
Numbers are typically doubles.
n1 := 10
n2 := 3.14
n3 := -5.0
Strings
greeting := "hello"
path := "D:\\Models\\sample.fcs"
Booleans
isEnabled := True
isValid := False
Lists
values := [1, 2, 3]
points := ["A", "B", "C"]
Objects
pt := { x = 1, y = 2 }
style := { color = "Red", thickness = 2 }
Null / undefined
Some expressions may evaluate to null or undefined depending on context, optional parameters, or conditional construction.
optionalNote := Null
Practical tip: when a value may be missing, guard downstream expressions with a conditional instead of assuming the value always exists.
Operators
Arithmetic
a := 10 + 2
b := 10 - 2
c := 10 * 2
d := 10 / 2
m := 10 % 3
p := 2 ** 3
Comparison
isEqual := a == b
isNotEqual := a != b
isSmall := a < b
isLarge := a > b
isSmallOrEqual := a <= b
isLargeOrEqual := a >= b
Logical
ok := isEnabled && isValid
fallback := hasMesh || hasGeometry
notReady := !isReady
Ternary
label := width > 5 ? "large" : "small"
Practical tip: ternaries are useful for optional geometry, alternate materials, and conditional output values without needing verbose control flow.
Functions and Lambdas
Lambdas are compact and are often used with lists.
square := x => x * x
add := (a, b) => a + b
More examples:
isTall := h => h > 3.0
scalePoint := (p, f) => { x = p.x * f, y = p.y * f }
Use lambdas when you want a reusable expression or when list operators expect a function argument.
List Operations (LINQ-style)
FCS supports collection-style operations that feel similar to LINQ.
items := [1, 2, 3, 4, 5]
sum := items.Sum
filtered := items.Where(x => x > 2)
mapped := items.Select(x => x * 2)
reduced := items.Aggregate(0, (s, x) => s + x)
count := items.Count
first := items.First
Typical patterns
widths := [2.5, 3.0, 4.5]
maxWidth := widths.Max
names := ["A", "B", "C"]
joined := names.Aggregate("", (s, x) => s == "" ? x : s + ", " + x)
validHeights := heights.Where(h => h > 0)
Practical tip: prefer
Where,Select, andAggregateover manually duplicating expressions. They keep parameterized models compact and easier to change.
Math and Units
FCS includes standard math functions and unit helpers.
s := Sin(angle)
r := Sqrt(x**2 + y**2)
angle := 45 * Unit.deg # degrees → radians
PI # built-in constant
Common math examples
hyp := Sqrt(dx**2 + dy**2)
angleRad := 90 * Unit.deg
cosVal := Cos(angleRad)
Units tip
If an API expects radians, multiply degree values by Unit.deg instead of manually converting.
roofPitch := 30 * Unit.deg
rotated := GCS.Rx(roofPitch)
String Operations
Strings can be concatenated and transformed through methods.
msg := "Value: " + x.ToString()
url.Replace("?", "%3F")
More examples
memberId := "B-" + index.ToString()
title := "Zone " + zoneName
safeUrl := rawUrl.Replace(" ", "%20")
Practical tip: use string building for labels, file names, report text, and diagnostic output values.
Coordinate Systems
Coordinate systems are central to geometry placement and transformation.
GCS # Global Coordinate System
GCS.Tx(d), GCS.Ty(d), GCS.Tz(d) # Translation
GCS.Rx(a), GCS.Ry(a), GCS.Rz(a) # Rotation (radians)
Examples
moveUp := GCS.Tz(3.0)
moveRight := GCS.Tx(5.0)
spin := GCS.Rz(45 * Unit.deg)
These transforms are often combined with local coordinate systems, gblocks, and distributed elements.
Key Insight: Lazy Evaluation
Lazy evaluation is the biggest conceptual difference from most scripting languages.
x := 10
y := x * 2 # y is the EXPRESSION "x * 2", not the number 20
x := 5 # NOW y evaluates to 10 (not still 20!)
Mental model
x := 10defines a value or expression bindingy := x * 2stores the dependency onx- if
xchanges,yreflects the new result
Why this is useful
Lazy evaluation makes parameterized models powerful:
- changing one dimension updates derived geometry
- material or load formulas stay linked to source parameters
- repeated elements can share one source of truth
Example: parametric rectangle
width := 10
height := 8
area := width * height
perimeter := 2 * (width + height)
width := 12
# area now evaluates to 96
# perimeter now evaluates to 40
Practical tip: define primary driving parameters first, then derive everything else from them. This is the cleanest way to build reusable FCS models.
Best Practices
- Use
:=for top-level declarations and=inside config blocks. - Prefer derived expressions over hard-coded duplicate values.
- Use lists and lambdas for repeated logic.
- Apply
Unit.degwhen working with angles in degrees. - Use conditionals for optional features instead of branching mentally across multiple files.
- Keep core dimensions centralized so lazy evaluation works in your favor.
Quick Reference
# Variables
x := 10
obj := MyType{ A = 1 }
# Types
n := 3.14
s := "hello"
b := True
l := [1, 2, 3]
o := { x = 1, y = 2 }
# Lambdas
square := x => x * x
# Lists
vals := [1, 2, 3]
vals.Select(x => x * 2)
vals.Where(x => x > 1)
vals.Sum
# Math
angle := 45 * Unit.deg
Sin(angle)
Sqrt(9)
# Coordinates
GCS.Tx(1)
GCS.Rz(90 * Unit.deg)
Final Takeaway
If you remember only one thing from this page, remember this:
Top-level FCS is lazy. Write models so derived values depend on source parameters, and always be deliberate about := versus =.