Field From Expression component icon

Field From Expression

Nickname FieldFromExpMorphe › 1. Field › CreateOutputs Field

Builds a field from a math expression you type, using the point coordinates x, y and z, your own inputs, vectors, matrices, noise and domain operations. The expression is compiled once and then evaluated at native speed, the same cost as a built-in primitive.

Quick start

  1. Place Field From Expression from the 1. Field panel. The default expression is a unit sphere: sqrt(x*x + y*y + z*z) - 1.
  2. To preview it, wire the output into Preview Field 3D. To mesh it, give it a box with Implicit From Field and wire that into Mesh From Implicit.
  3. Click + on the component to add a number input. Right-click the new input, choose Set Nickname, and use that name in the expression: sqrt(x*x + y*y + z*z) - r.
  4. Right-click the component for typed inputs:
    • Add Field input takes any other field (a sphere, a mesh, a volume, another expression) and uses its value at each point.
    • Add Point input and Add Vector input bind a point or vector as a vec3. Name one p and write length(vec3(x, y, z) - p) - r for a sphere centred on that point.

Inputs

NameNickTypeDefaultDescription
ExpressionExTextsqrt(x*x + y*y + z*z) - 1Math expression

Outputs

NameNickTypeDescription
FieldFFieldParsed expression as a Field.

Example definition

A gyroid sheet lattice from an expression
A gyroid written as an expression, thickened into a sheet with abs(...) - t, clipped to a 40 mm cube and meshed.
Try
  • Drag f to change the cell size: the period is 2π / f.
  • Drag t to thicken or thin the walls.
  • Replace the expression with one from the examples on this page.
Download field-from-expression.gh4 KB · Rhino 8 · Morphé 1.0

Syntax at a glance

You write It means
x, y, z The coordinates of the point being evaluated
PI, TAU, E π, 2π and Euler's number
2.5, 1e-3 Numbers
r, thickness Your own inputs, by nickname
vec2(a, b), vec3(a, b, c) Vectors
p.x, p.xy, p.zyx Components of a vector
+ - * / ^ Arithmetic; ^ is power
a < b, a == b, !a Comparisons and logic, giving 1 or 0
c ? a : b a when c is non-zero, otherwise b
let q = … in … Name a value for the rest of the expression
sin(x), smin(a, b, k) Functions: the full list is below

The result of the whole expression must be a single number: the signed distance, negative inside the shape. Vectors and matrices can only be intermediate values.

Reserved names

x, y and z are the Cartesian coordinates of the point. PI (3.14159…), TAU (6.28318…, that is 2π) and E (2.71828…) are constants. These six names are always available and cannot be used for an input or bound with let: an input with one of these names is refused with an error asking you to rename it.

Types

Type How to make it Notes
scalar numbers, coordinates, Number inputs The default for everything
vec2 vec2(a, b) Two scalars
vec3 vec3(a, b, c), or a Point or Vector input Three scalars
mat2 mat2(col0, col1) Two vec2 columns
mat3 mat3(col0, col1, col2) or mat3_from_axis_angle(axis, angle) Three vec3 columns

Operators

Arithmetic

+ - * / ^, a leading - to negate, and a leading +, which does nothing. Power is right-associative: 2^3^2 is 2^(3^2).

Operators work element by element on vectors. A scalar combined with a vector applies to every component: vec3(x, y, z) * 2. Mixing a vec2 with a vec3 is an error. With a matrix, * is matrix multiplication (see Matrices).

Powers and negative numbers

A whole-number exponent works for negative bases: x^2, x^-2, or an input or let name that holds a whole number up to 63. Any other exponent, such as x^0.5 or a computed x^(1+1), is calculated through logarithms and gives NaN for a negative base. pow(a, b) follows the same rule.

Negating a power

A leading minus binds tighter than ^, so -x^2 means (-x)^2, which equals x^2. To negate a power, bracket it: exp(-(x^2)), or subtract it from zero: exp(0 - x^2). A negative exponent needs no brackets: 2^-x is 2^(-x).

Comparisons, logic and conditions

  • <, <=, >, >=, == and != compare two values and give 1 (true) or 0 (false).
  • && (and), || (or) and ! (not) combine them. Any non-zero value counts as true.
  • cond ? a : b gives a when cond is non-zero, otherwise b. The branches can be scalars or vectors of the same width; the condition must be a scalar, so reduce a vector comparison first, as in (p > q).x.
  • Comparisons work element by element on vectors, like arithmetic.
  • Comparisons don't chain: write 0 < x && x < 10, not 0 < x < 10.

Precedence, from lowest to highest: ?:, then ||, &&, == and !=, < <= > >=, + and -, * and /, ^, a leading - + or !, and finally . for components.

Hard switches leave seams

Comparisons, conditions and step jump from 0 to 1. If that jump decides where the surface is, the field has a break in it and the mesh shows a crack or seam. For blends that change across space, use the smooth tools mix, smoothstep, clamp, smin and smax, and keep hard conditions for logic that doesn't define the surface directly.

Components of a vector

Read or reorder a vector's components by name, using any combination of x, y and z, one to three letters long, with repeats allowed:

Form Gives Examples
1 letter scalar p.x, p.z
2 letters vec2 p.xy, p.zx, p.xx
3 letters vec3 p.xyz, p.zyx, p.xxz

Each letter must exist on the vector (.z on a vec2 is an error), and there is no four-component vector.

Naming values with let

let name = value in body names a value so you can use it in body. The value can be a scalar, a vector or a matrix, and the body runs to the closing bracket or the end of the expression:

let q = twist(vec3(x, y, z), k) in length(q.xy) - r

Bindings can be nested, and a later one can use an earlier one: let a = x * 2 in let b = a + 1 in b - z. Inside its body, a let name hides an input with the same name.

Functions

Scalar functions

These take numbers, and also work element by element on vectors.

Function What it does
sin cos tan Trigonometry, in radians
asin acos atan Inverse trigonometry, in radians
atan2(y, x) Arctangent that knows the quadrant
sinh cosh tanh Hyperbolic functions
sqrt Square root
cbrt Cube root, smooth through zero
abs Absolute value
sign −1, 0 or +1, smooth through zero
exp e to the power
ln, log Natural logarithm (the two are the same)
log2, log10 Base-2 and base-10 logarithms
floor ceil round fract Rounding; round takes halves away from zero, so round(-2.5) is −3
min(a, b), max(a, b) Smaller and larger of two values
mod(a, b) Remainder of a / b, never negative: mod(-1, 3) is 2
clamp(x, lo, hi) Limit x to the range lo to hi
mix(a, b, t) Blend: (1 − t)·a + t·b
smin(a, b, k), smax(a, b, k) Smooth min and max with a fillet radius k (see below)
pow(a, b) a to the power b, same rules as ^
square(a) a · a
step(edge, x) 0 below edge, 1 above, a hard switch
smoothstep(e0, e1, x) 0 below e0, 1 above e1, a smooth S-curve between

smin and smax are the smooth union and intersection. k is the same fillet radius as the Radius input on the boolean components: two surfaces meeting at a right angle get a round fillet of radius k, and as k approaches 0 they become plain min and max. They are polynomial, so they stay safe to mesh: use them rather than an exponential blend, which can overflow.

Vector functions

Function Gives What it does
length(v) scalar Length of a vector
distance(a, b) scalar length(a - b)
dot(a, b) scalar Dot product of two vectors of the same width
cross(a, b) vec3 Cross product of two vec3
normalize(v) vector Unit vector, safe at the origin
reflect(I, N) vector Reflect I in the plane with normal N
refract(I, N, eta) vector Refract I through a surface with index ratio eta; zero on total internal reflection
faceforward(N, I, Nref) vector N, flipped if needed to face against Nref

Matrices

Function What it does
mat2(c0, c1), mat3(c0, c1, c2) Build a matrix from its column vectors
mat3_from_axis_angle(axis, angle) Rotation of angle radians about axis; the axis needn't be unit length
transpose(M) Transpose of a 2×2 or 3×3 matrix
inverse(M) Inverse of a 2×2 or 3×3 matrix
outer(a, b) Outer product of two vectors of the same width

Use * to apply them: M * v transforms a vector, M * N combines two matrices, and M * s scales every element. To transform a shape, evaluate it at the inverse-transformed point: rotate by evaluating at inverse(M) * vec3(x, y, z). For a pure rotation, transpose(M) is the same as the inverse and cheaper.

Noise

value_noise(p), perlin(p) and simplex(p) give smooth noise between roughly −1 and 1, where p is a vec2 or vec3. An optional second argument is a whole-number seed: simplex(p, 7). All three are continuous, so safe to mesh, and give the same result every time.

  • value_noise is cheap and blocky.
  • perlin is smooth gradient noise.
  • simplex is smooth with no grid direction, usually the best choice for organic forms.

Scale the point to set the feature size: simplex(vec3(x, y, z) * 0.2) has features about five units across.

Domain operations

These take a point and return a transformed point, so you can build a shape once and repeat, mirror, twist or bend it. They cost the same as writing the maths out by hand.

Function What it does
mirror_x(p), mirror_y(p), mirror_z(p) Mirror in the plane through the origin normal to that axis
repeat(p, c) Tile space every c units; c is a number, or a vec3 for each axis
radial_repeat(p, n) n-fold rotational copies around Z; n is a whole number of 1 or more
twist(p, k) Twist around Z by k radians per unit of height
bend(p, k) Bend in the XZ plane, in proportion to x
rotate_x(p, a), rotate_y(p, a), rotate_z(p, a) Rotate about an axis, right-handed
rotate_axis(p, axis, a) Rotate about any axis
polar(p) vec2(r, θ) from p.xy
cylindrical(p) vec3(r, θ, z)
spherical(p) vec3(r, θ, φ)

Examples

Every expression below can be pasted straight into the Expression input. Names in the Inputs column are Number inputs unless marked otherwise; add them with + and set their nicknames.

Basic shapes

Shape Expression Inputs
Sphere of radius 1 length(vec3(x, y, z)) - 1
Sphere of any radius length(vec3(x, y, z)) - r r
Ellipsoid length(vec3(x/rx, y/ry, z/rz)) - 1 rx, ry, rz
Torus length(vec2(length(vec2(x, y)) - R, z)) - r R, r
Box, half-sizes a, b, c max(max(abs(x) - a, abs(y) - b), abs(z) - c) a, b, c
Infinite cylinder along Z length(vec2(x, y)) - r r
Ground plane z

Combining shapes

Shape Expression Inputs
Union of a sphere and a box min(length(vec3(x, y, z)) - 6, max(max(abs(x) - 8, abs(y) - 3), abs(z) - 3))
The same, with a smooth fillet smin(length(vec3(x, y, z)) - 6, max(max(abs(x) - 8, abs(y) - 3), abs(z) - 3), k) k
Hollow sphere, wall t abs(length(vec3(x, y, z)) - r) - t r, t
Sphere with a cylindrical hole max(length(vec3(x, y, z)) - 6, -(length(vec2(x, y)) - 2))

Repeating, mirroring and twisting

Shape Expression Inputs
Two mirrored spheres length(mirror_x(vec3(x, y, z)) - vec3(d, 0, 0)) - 1 d
Six spheres in a ring length(radial_repeat(vec3(x, y, z), 6) - vec3(2, 0, 0)) - 1
Endless grid of spheres length(repeat(vec3(x, y, z), 4)) - 1
Twisted bar max(max(abs(twist(vec3(x, y, z), k).x) - 0.5, abs(twist(vec3(x, y, z), k).y) - 0.1), abs(z) - 1) k
Sphere winding in a helix length(twist(vec3(x, y, z), k) - vec3(2, 0, 0)) - 1 k

Lattices

Triply periodic minimal surfaces repeat every 2π / f units. Use abs(…) - t to turn the surface into a wall of thickness about t, then clip it with Implicit From Field or wrap it with Conform To Surface.

Lattice Expression Inputs
Gyroid sin(x*f)*cos(y*f) + sin(y*f)*cos(z*f) + sin(z*f)*cos(x*f) f
Gyroid sheet abs(sin(x*f)*cos(y*f) + sin(y*f)*cos(z*f) + sin(z*f)*cos(x*f)) - t f, t
Schwarz P cos(x*f) + cos(y*f) + cos(z*f) f
Schwarz D sin(x*f)*sin(y*f)*sin(z*f) + sin(x*f)*cos(y*f)*cos(z*f) + cos(x*f)*sin(y*f)*cos(z*f) + cos(x*f)*cos(y*f)*sin(z*f) f

These surfaces aren't exact distance fields, so mesh them with a finer MinFeatureSize than you would a solid of the same size.

Grading, noise and texture

Shape Expression Inputs
Rippled sphere length(vec3(x, y, z)) - 1 + sin(x*8)*cos(y*8)*0.05
Ripples you can tune length(vec3(x, y, z)) - 1 + sin(x*freq)*amp freq, amp
Radius graded along x length(vec3(x, y, z)) - mix(0.5, 1.5, smoothstep(-2, 2, x))
Gyroid walls thickening with height abs(sin(x*f)*cos(y*f) + sin(y*f)*cos(z*f) + sin(z*f)*cos(x*f)) - mix(0.1, 0.8, clamp(z / h, 0, 1)) f, h
Organic surface from noise length(vec3(x, y, z)) - 1 + simplex(vec3(x, y, z)*f)*amp f, amp
Sphere eaten away by noise max(length(vec3(x, y, z)) - 2, simplex(vec3(x, y, z)*3))

Points, let and matrices

Shape Expression Inputs
Sphere centred on a point length(vec3(x, y, z) - p) - r p (Point), r
A second sphere at a point min(length(vec3(x, y, z)) - 1, length(vec3(x, y, z) - p) - 0.3) p (Point)
Twisted tube, using let let q = twist(vec3(x, y, z), k) in length(q.xy) - r k, r
Sphere rotated about Z by a matrix let M = mat3_from_axis_angle(vec3(0, 0, 1), a) in length(inverse(M) * vec3(x, y, z) - vec3(2, 0, 0)) - 1 a
A different shape on each side (leaves a seam) x > 0 ? length(vec3(x, y, z)) - 1 : abs(y) - 0.3

Combining other fields

These use Add Field input. Each name is the nickname of a Field input wired from another component.

Result Expression Inputs
Union of two fields min(a, b) a, b (Field)
Smooth union smin(a, b, k) a, b (Field), k
Smooth intersection smax(a, b, k) a, b (Field), k
Subtract b from a max(a, -b) a, b (Field)
Shell of a field abs(a) - thickness a (Field), thickness
Blend between two fields a + t * (b - a) a, b (Field), t
Texture on an imported mesh m + sin(x*freq)*cos(y*freq)*amp m (Field, from Implicit From Mesh), freq, amp
Sphere pushed out by a mesh's distance length(vec3(x, y, z)) - 1 + 0.1 * m m (Field)

The last two are the reason Field inputs exist: wire any mesh-based field in as m and add procedural detail to it, with no scripting.

Performance

  • When every input is a number, or a field built from primitives and arithmetic, the expression compiles to native code and runs as fast as a built-in primitive.
  • When an input is a mesh, a volume or another sampled field, the expression is evaluated point by point instead. That is slower, so keep those inputs for where you need them.
  • A Number input becomes a constant in the compiled expression, so dragging its slider recompiles the expression at every step.
  • Preview and mesh resolution are set downstream: MinFeatureSize on Mesh From Implicit, and the preview feature size in the right-click menu of Implicit From Field.

Error messages

Mistakes in the expression show as a red balloon on the component, with the column where the problem starts.

Cause Example message
Syntax error unexpected token '<end of input>' at column 4
Chained comparison '<' and '<' do not chain: write 'a < b && b < c', …
Unknown function unknown function 'tanhh' at column 1
Unknown name unknown identifier 'foo' at column 7
Wrong number of arguments function 'sin' expects 1 argument but got 2 at column 1
Vector widths don't match mismatched widths in '+': cannot broadcast vec2 to width 3
Result is a vector expression must produce a scalar (signed distance), not vec3
Component of a number component access '.x' requires a vector (vec2 or vec3), got scalar
Swizzle too long swizzle '.xyzx' has unsupported width 4
Matrix where a number is needed '+' does not accept a mat3 argument
Reserved name in let cannot bind reserved name 'x' in a let-expression at column 1
Input with a reserved name Input 'E' uses a reserved name (x, y, z, PI, E, TAU). Rename it.

Messages

What the component may report, shown as a balloon on the canvas.

LevelMessage
ErrorExpression is empty.
Written for Morphé 1.0 · Rhino 8