Operators
Convex-λ is built on a rich operator system that often behaves differently depending on the types of the operands (Polymorphism). This allows for concise and expressive geometric logic.
This chapter details every operator with extensive examples.
Mathematical Operators
Addition +
Used for arithmetic addition, vector translation, and array concatenation.
1. Number + Number Standard addition.
sum = 10 + 5 // 15
assert(sum == 15, "Add Num")
2. Vector + Vector Component-wise addition.
v1 = Vector(1, 0, 0)
v2 = Vector(0, 1, 0)
v3 = v1 + v2 // Vector(1, 1, 0)
assert(v3 == Vector(1, 1, 0), "Add Vec")
3. Point + Vector Translates a point by a vector.
p = Point(0, 0, 0)
v = Vector(0, 0, 5)
p_new = p + v // Point(0, 0, 5)
assert(p_new == Point(0, 0, 5), "Point Translate")
4. Array + Array Concatenates two arrays.
arr1 = [1, 2]
arr2 = [3, 4]
arr3 = arr1 + arr2 // [1, 2, 3, 4]
assert(arr3.length == 4, "Array Concat")
5. Array + Scalar (Broadcast) Adds the scalar to every element of the array.
nums = [10, 20, 30]
result = nums + 5 // [15, 25, 35]
assert(result[0] == 15, "Array Broadcast +")
Subtraction -
Used for arithmetic subtraction, vector difference, and unary negation.
1. Number - Number
diff = 10 - 3 // 7
assert(diff == 7, "Sub Num")
2. Vector - Vector
v1 = Vector(1, 1, 1)
v2 = Vector(0, 0, 1)
v3 = v1 - v2 // Vector(1, 1, 0)
assert(v3 == Vector(1, 1, 0), "Sub Vec")
3. Unary Negation Negates numbers or vectors.
n = -10
v = -Vector(1, 0, 0) // Vector(-1, 0, 0)
assert(v.x == -1, "Negate Vec")
4. Array Subtraction (Broadcast)
arr = [10, 20, 30]
res = arr - 5 // [5, 15, 25]
assert(res[0] == 5, "Array Broadcast -")
Multiplication *
Used for scaling, dot products, symmetry composition, and intersection.
1. Number * Number
prod = 6 * 7 // 42
assert(prod == 42, "Mul Num")
2. Vector * Number Scales the vector.
v = Vector(1, 0, 0)
scaled = v * 5 // Vector(5, 0, 0)
assert(scaled.x == 5, "Scale Vec")
3. Plane * Plane (Intersection) Returns the Line where two planes intersect.
pl1 = Plane(Vector(1,0,0), 0) // YZ plane
pl2 = Plane(Vector(0,1,0), 0) // XZ plane
line = pl1 * pl2 // Line along Z axis
assert(line.dir.z == 1 || line.dir.z == -1, "Plane Intersect")
4. Line * Plane (Intersection) Returns the Point where a line hits a plane.
line = Line(Point(0,0,10), Vector(0,0,-1))
plane = Plane(Vector(0,0,1), 0)
hit = line * plane // Point(0,0,0)
assert(hit == Point(0,0,0), "Line Plane Hit")
5. Symmetry Group Composition Combines two symmetry operations.
// Rotate * Mirror composition
g = Rotate(Vector(0,0,1), 4) // * Mirror(...)
Division /
Used for arithmetic division and scaling.
1. Number / Number
quo = 10 / 2 // 5
assert(quo == 5, "Div Num")
2. Vector / Number Scales the vector by 1/N.
v = Vector(10, 0, 0)
scaled = v / 2 // Vector(5, 0, 0)
assert(scaled.x == 5, "Div Vec")
3. Point / Number Scales the point's coordinates (relative to origin).
p = Point(10, 10, 10)
scaled = p / 2 // Point(5, 5, 5)
assert(scaled.x == 5, "Div Point")
4. Array / Number (Broadcast)
arr = [10, 20, 30]
res = arr / 10 // [1, 2, 3]
assert(res[0] == 1, "Div Array")
5. Midpoint Calculation
Combining + and / to find a midpoint.
p1 = Point(0, 0, 0)
p2 = Point(10, 10, 10)
mid = (p1 + p2) / 2 // Point(5, 5, 5)
assert(mid.x == 5, "Midpoint")
Modulo / Cross Product %
Overloaded for remainder and vector cross product.
1. Number % Number Euclidean remainder.
rem = 10 % 3 // 1
assert(rem == 1, "Mod")
2. Negative Modulo Handles negative numbers correctly (Euclidean).
val = -10 % 3 // 2 (not -1)
assert(val == 2, "Euclidean Mod")
3. Array Broadcast
nums = [10, 11, 12]
mods = nums % 2 // [0, 1, 0]
assert(mods[1] == 1, "Mod Array")
Power / Distance ^
Overloaded for exponentiation and distance calculation.
1. Number ^ Number Exponentiation.
sq = 5 ^ 2 // 25
assert(sq == 25, "Pow")
2. Vector ^ Vector (Distance) Euclidean distance between two vectors tips.
v1 = Vector(0, 0, 0)
v2 = Vector(3, 4, 0)
dist = v1 ^ v2 // 5
assert(dist == 5, "Vec Dist")
3. Point ^ Point (Distance) Distance between two points.
p1 = Point(0, 0, 0)
p2 = Point(1, 1, 1)
dist = p1 ^ p2 // sqrt(3) ~ 1.732
assert(abs(dist - 1.732) < 0.001, "Point Dist")
4. Array Broadcast
nums = [1, 2, 3]
squares = nums ^ 2 // [1, 4, 9]
assert(squares[2] == 9, "Pow Array")
Projection >>
Projects a point onto a plane.
1. Point >> Plane Projects the point onto the plane surface along the normal.
pt = Point(0, 0, 10)
pl = Plane(Vector(0,0,1), 0) // XY Plane
// Projects pt onto pl
proj = pt >> pl
assert(proj.z == 0, "Projection")
2. Array >> Plane Projects multiple points.
pts = [Point(0,0,1), Point(0,0,2)]
pl = Plane(Vector(0,0,1), 0)
flat = pts >> pl
assert(flat[0].z == 0, "Array Projection")
Logical Operators
Returns true or false.
Comparison
==, !=, <, >, <=, >=.
1. Number Comparison
check = 10 > 5 // true
assert(check, "GT")
2. Equality Check
is_equal = 5 == 5 // true
assert(is_equal, "EQ")
3. Filtering Logic Used inside filters.
x = 10
assert(x > 0, "Check")
4. Boolean Logic
&& (AND), || (OR), ! (NOT).
x = 5
valid = (x > 0) && (x < 10)
assert(valid, "Logic")
5. Ternary Operator ? :
Conditional value selection.
x = 5
val = (x > 0) ? "Positive" : "Negative"
assert(val == "Positive", "Ternary")
Pipe Operators
Map / Apply |>
Passes the left operand as the first argument to the function on the right.
1. Function Application
x = 16 |> sqrt // same as sqrt(16) -> 4
assert(x == 4, "Pipe Func")
2. Array Mapping Applies the function to every element.
nums = [1, 4, 9]
roots = nums |> sqrt // [1, 2, 3]
assert(roots[1] == 2, "Pipe Map")
3. Symmetry Application Applies a symmetry group to geometry.
p = Point(1, 0, 0)
rot = Rotate(Vector(0,0,1), 4)
// Generates 4 points
sym_pts = p |> rot
assert(sym_pts.length == 4, "Symmetry Apply")
4. Chained Operations Reading left-to-right.
val = -16 |> abs |> sqrt
assert(val == 4, "Pipe Chain")
5. Lambda Application
nums = [1, 2, 3]
doubled = nums |> (x) => x * 2 // [2, 4, 6]
assert(doubled[2] == 6, "Pipe Lambda")
Filter ?|
Selects elements from an array that satisfy a condition.
1. Basic Filtering
nums = [1, 2, 3, 4, 5]
odds = nums ?| (x) => x % 2 != 0 // [1, 3, 5]
assert(odds.length == 3, "Filter Basic")
2. Geometric Filtering Find points above the equator.
pts = [Point(0,0,1), Point(0,0,-1)]
upper = pts ?| (p) => p.z > 0
assert(upper.length == 1, "Filter Geo")
3. Type Filtering (Advanced usage with type checks)
4. Range Filtering
nums = 1 .. 10
high = nums ?| (x) => x > 8 // [9, 10]
assert(high.length == 2, "Filter Range")
5. Combined with Map Filter then Map.
result = (1 .. 10) ?| ((x) => x > 5) |> (x) => x * 10
assert(result[0] == 60, "Filter Map")
Geometric Operators
Bind :
Binds a Normal Vector and a Point (or distance) to create a Plane.
1. Normal : Point
Creates a plane with normal n passing through point p.
n = Vector(0, 0, 1)
p = Point(0, 0, 5)
pl = n : p // Plane(0,0,1, dist=5)
assert(pl.distance == 5, "Bind Point")
2. Normal : Distance (Number)
Usually done via constructor Plane(n, d), but : works if overloaded (check specific implementation). Typically used with Points.
3. Array : Point Creates multiple planes passing through one point.
normals = [Vector(1,0,0), Vector(0,1,0)]
p = Point(0,0,0)
planes = normals : p
assert(planes.length == 2, "Bind Array Normal")
4. Normal : Array Creates parallel planes.
n = Vector(0,0,1)
pts = [Point(0,0,0), Point(0,0,10)]
planes = n : pts
assert(planes.length == 2, "Bind Array Point")
5. Auto-Centering If the point is the origin (0,0,0), it creates a center-point cut.
pl = Vector(1,1,1) : Point(0,0,0)
Sweep / Auto-Note ->
Similar to Bind, but implies a "cutting" action and often attaches automatic documentation notes (like "Meet A", "Set Girdle").
1. Normal -> Distance
Creates a plane at distance d.
pl = Vector(0,0,1) -> 5
assert(pl.distance == 5, "Sweep Dist")
2. Normal -> Point
Creates a plane through point p.
pl = Vector(0,0,1) -> Point(0,0,5)
assert(pl.distance == 5, "Sweep Point")
3. Auto-Note: Set Girdle
If the cut defines the girdle width, -> detects it.
girdle_pl = Vector(1,0,0) -> 10
// plane.notes is automatically set to "Set stone size" or "Set girdle width"
4. Auto-Note: Meet
If the target point was derived from an intersection, -> generates a "Meet ..." note.
pl1 = Plane(Vector(1,0,0), 1)
pl2 = Plane(Vector(0,1,0), 1)
pl3 = Plane(Vector(0,0,1), 1)
meet_pt = pl1 * pl2 * pl3
cut = Vector(1,1,1) -> meet_pt
// plane.notes = "Meet [Plane names...]"
5. Chaining
pl1 = Plane(Vector(1,0,0), 1)
pl2 = Plane(Vector(0,1,0), 1)
pl3 = Plane(Vector(0,0,1), 1)
final_plane = Vector(0,0,1) -> (pl1 * pl2 * pl3)
Contact *>
Finds the "Contact Point" or "Tangent Point". It searches a set of geometry for the point most extreme in the direction of a vector.
*1. Vector > Stone
Finds the vertex on the current stone that is furthest in direction v.
_ := Cube(2)
// Find the tip of the stone (highest Z)
tip = Vector(0,0,1) *> Stone
assert(tip.z == 1, "Contact Stone")
*2. Vector > Array
Finds the point in the array that maximizes dot product with v.
pts = [Point(0,0,0), Point(10,10,10)]
v = Vector(1,1,1)
best = v *> pts // Point(10,10,10)
assert(best.x == 10, "Contact Array")
3. Used for Meet-point Faceting "Cut until you touch this vertex".
_ := Cube(2)
target_v = Vector(0,0,1) *> Stone
new_cut = Vector(1,0,1) -> target_v
*4. Vector > Plane (Usually undefined unless bounded, behavior depends on implementation).
5. Finding Girdle Corners
_ := Cube(2)
corner = Vector(1,0,0) *> Stone
Range Operator ..
Creates an array of integers.
1. Basic Range
r = 1 .. 5 // [1, 2, 3, 4, 5]
assert(r.length == 5, "Range Len")
2. Loop Indexing Useful for creating sequences.
angles = 0 .. 3 |> (i) => i * 90
assert(angles[3] == 270, "Range Loop")
3. Creating Grids
x_vals = 0 .. 10
y_vals = 0 .. 10
4. With Filter
evens = (1 .. 10) ?| (x) => x % 2 == 0
assert(evens.length == 5, "Range Filter")
5. Negative Ranges
r = -3 .. 3 // [-3, -2, -1, 0, 1, 2, 3]
assert(r.length == 7, "Neg Range")