| name | sverchok-impl-parametric |
| description | Use when designing parametric AEC geometry in Sverchok -- structural grids, facade panels, parametric stairs, roof geometry, MEP routing, or terrain from data. Prevents the common mistake of not using matrix transforms for element placement (using vertex offsets instead, which breaks with rotation). Covers matrix-based transforms, Blender object integration, and data-driven geometry generation. Keywords: parametric design, structural grid, facade panel, stairs, roof, terrain, matrix transform, data-driven, AEC geometry, Sverchok parametric, generate stairs, create facade, parametric building.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires Blender 4.0+/5.x with Sverchok v1.4.0+. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
sverchok-impl-parametric
Quick Reference
What This Skill Covers
Parametric AEC (Architecture, Engineering, Construction) design workflows using Sverchok's node-based system. Build structural grids, facade systems, stairs, roofs, MEP routing, and terrain from data — all as reusable parametric node trees.
- Structural grids: column grids, beam layouts, floor plates from spacing parameters
- Facade systems: panel division, curtain wall mullions, louver arrays
- Parametric stairs: straight, L-shaped, spiral with tread/riser profiles
- Roof geometry: hip, gable, shed from footprint polygons
- MEP routing: pipe and duct paths using spline-based nodes
- Terrain: contour/point cloud data to mesh surfaces
- Supporting techniques: matrices, Blender object integration, CSV/JSON data input, NumPy acceleration
Critical Warnings
NEVER generate geometry without wrapping output in the Sverchok nesting convention — ALL socket data requires [[object_data]] wrapping. Flat vertex lists corrupt downstream nodes.
NEVER build parametric node trees without tree.init_tree() context manager — every node/link addition without it triggers O(n²) updates.
NEVER hardcode absolute coordinates for AEC elements — ALWAYS use matrix transforms so elements respond to parameter changes. Hardcoded positions break parametric intent.
NEVER use deepcopy=False on sv_get() when mutating vertex data for transforms — this corrupts upstream cached data shared across all downstream nodes.
ALWAYS use match_long_repeat when combining inputs of different lengths (e.g., grid positions × profile shapes) — without it, shorter lists silently truncate.
ALWAYS check output.is_linked before computing geometry — AEC node trees with many outputs waste significant time computing unused branches.
Decision Tree
What AEC element do you need?
├── Structural grid (columns, beams, floors)
│ ├── Regular grid → Number Range + List Cross → Matrix Apply on profile
│ ├── Irregular grid → CSV In with column positions → Matrix Apply
│ └── Multi-story → Add Z-offset per floor via Matrix Multiply
│
├── Facade panels / curtain wall
│ ├── Flat panels → Subdivide face → Inset → Extrude per cell
│ ├── Louvers → Line + Array via Matrix → rotate per element
│ └── Data-driven → CSV/JSON In → Map Range for panel parameters
│
├── Stairs
│ ├── Straight → Number Range for treads → Box + Matrix offset
│ ├── L-shape → Two straight runs + landing platform
│ └── Spiral → Trigonometric SNLite + Matrix rotation per step
│
├── Roof from footprint
│ ├── Simple gable → Extrude edge + move ridge vertices up
│ ├── Hip roof → Straight Skeleton or inset + raise center
│ └── Shed/mono-pitch → Offset one edge upward
│
├── MEP routing
│ ├── Simple pipe run → Polyline → Pipe node (circle profile + sweep)
│ ├── Duct with bends → Bezier/NURBS spline → Rectangular profile sweep
│ └── From coordinates → CSV In → Vector In → Polyline Viewer
│
└── Terrain from data
├── Point cloud → Delaunay 2D triangulation
├── Contour lines → Interpolate Z → Surface from curves
└── Grid DEM → CSV In → Plane grid → set Z from data
Essential Patterns
Pattern 1: Structural Column Grid
Generate a parametric column grid from X/Y spacing and column profile.
import bpy
from mathutils import Matrix, Vector
tree = bpy.data.node_groups.new("ColumnGrid", 'SverchCustomTreeType')
with tree.init_tree():
range_x = tree.nodes.new('SvGenNumberRange')
range_x.location = (0, 200)
range_x.mode = 'RANGE_COUNT'
range_y = tree.nodes.new('SvGenNumberRange')
range_y.location = (0, 0)
range_y.mode = 'RANGE_COUNT'
cross = tree.nodes.new('SvListInputNode')
cross.location = (200, 100)
cyl = tree.nodes.new('SvCylinderNodeMK2')
cyl.location = (200, -100)
viewer = tree.nodes.new('SvViewerDrawMk4')
viewer.location = (600, 0)
tree.force_update()
Pattern 2: SNLite Column Grid (Complete)
Use SNLite for full control over a parametric column grid.
"""
in span_x s default=6.0
in span_y s default=6.0
in count_x s default=4
in count_y s default=3
in col_radius s default=0.15
in col_height s default=3.0
out verts v
out faces s
"""
import numpy as np
from math import pi, cos, sin
nx, ny = int(count_x), int(count_y)
r, h = col_radius, col_height
seg = 12
angles = np.linspace(0, 2 * pi, seg, endpoint=False)
cx = r * np.cos(angles)
cy = r * np.sin(angles)
col_v = []
for z in [0.0, h]:
for i in range(seg):
col_v.append((cx[i], cy[i], z))
col_f = []
for i in range(seg):
j = (i + 1) % seg
col_f.append((i, j, j + seg, i + seg))
col_f.append(list(range(seg)))
col_f.append(list(range(seg, 2 * seg)))
all_verts = []
all_faces = []
for ix in range(nx):
for iy in range(ny):
ox = ix * span_x
oy = iy * span_y
offset = len(all_verts)
for v in col_v:
all_verts.append((v[0] + ox, v[1] + oy, v[2]))
for f in col_f:
all_faces.append(tuple(fi + offset fi f))
verts = [all_verts]
faces = [all_faces]
Pattern 3: Matrix-Based Element Placement
Use Sverchok matrix nodes to place AEC elements at grid positions.
from mathutils import Matrix, Vector
import math
def create_placement_matrices(positions, rotation_z=0.0):
"""Create 4x4 transform matrices for element placement.
Parameters:
positions: list of (x, y, z) tuples
rotation_z: rotation in radians around Z axis
Returns:
list of Matrix — one per position, Sverchok-nested [[mat1, mat2, ...]]
"""
rot = Matrix.Rotation(rotation_z, 4, 'Z')
matrices = []
for pos in positions:
loc = Matrix.Translation(Vector(pos))
matrices.append(loc @ rot)
return [matrices]
Pattern 4: Facade Panel Division
Subdivide a wall face into parametric panels.
"""
in wall_w s default=12.0
in wall_h s default=3.0
in panels_x s default=6
in panels_y s default=2
in gap s default=0.02
out verts v
out faces s
"""
nx, ny = int(panels_x), int(panels_y)
pw = wall_w / nx
ph = wall_h / ny
g = gap
all_v = []
all_f = []
for ix in range(nx):
for iy in range(ny):
x0 = ix * pw + g
x1 = (ix + 1) * pw - g
z0 = iy * ph + g
z1 = (iy + 1) * ph - g
idx = len(all_v)
all_v.extend([(x0, 0, z0), (x1, 0, z0), (x1, 0, z1), (x0, 0, z1)])
all_f.append((idx, idx+1, idx+2, idx+3))
verts = [all_v]
faces = [all_f]
Pattern 5: Parametric Staircase
Generate a straight staircase from riser height and tread depth.
"""
in width s default=1.2
in riser_h s default=0.17
in tread_d s default=0.28
in num_steps s default=16
out verts v
out faces s
"""
n = int(num_steps)
w = width
rh = riser_h
td = tread_d
all_v = []
all_f = []
for i in range(n):
x0 = i * td
x1 = x0 + td
z0 = i * rh
z1 = z0 + rh
idx = len(all_v)
all_v.extend([(x0, 0, z0), (x0, w, z0), (x0, w, z1), (x0, 0, z1)])
all_f.append((idx, idx+1, idx+2, idx+3))
idx2 = len(all_v)
all_v.extend([(x0, 0, z1), (x0, w, z1), (x1, w, z1), (x1, 0, z1)])
all_f.append((idx2, idx2+1, idx2+2, idx2+3))
verts = [all_v]
faces = [all_f]
Pattern 6: Data-Driven Generation from CSV
Read element positions from CSV and generate geometry.
import bpy
tree = bpy.data.node_groups.new("DataDrivenGrid", 'SverchCustomTreeType')
with tree.init_tree():
csv_node = tree.nodes.new('SvCSVInNode')
csv_node.location = (0, 0)
script = tree.nodes.new('SvScriptNodeLite')
script.location = (200, 0)
profile = tree.nodes.new('SvBoxNodeMk2')
profile.location = (200, -200)
mat_apply = tree.nodes.new('SvMatrixApplyJoinNode')
mat_apply.location = (400, 0)
viewer = tree.nodes.new('SvViewerDrawMk4')
viewer.location = (600, 0)
tree.force_update()
Pattern 7: Terrain from Point Data
Generate terrain mesh from elevation data.
"""
in points v
out verts v
out faces s
"""
from mathutils.geometry import delaunay_2d_cdt
pts_2d = [(p[0], p[1]) for p in points]
edges_in = []
faces_in = []
result = delaunay_2d_cdt(pts_2d, edges_in, faces_in, 0, 1e-6)
out_verts_2d, out_edges, out_faces, _, _, _ = result
import numpy as np
pts_arr = np.array(points)
out_v = []
for v2d in out_verts_2d:
dists = (pts_arr[:, 0] - v2d[0])**2 + (pts_arr[:, 1] - v2d[1])**2
nearest = np.argmin(dists)
out_v.append((v2d[0], v2d[1], pts_arr[nearest, 2]))
verts = [out_v]
faces = [list(out_faces)]
Common Operations
AEC Element Node Recipes
| AEC Element | Key Nodes | Connection Pattern |
|---|
| Column grid | Number Range × 2, SNLite, Cylinder, Matrix Apply | Ranges → Cross product → Matrices → Apply to profile |
| Floor plate | Number Range × 2, Plane, Matrix Apply | Ranges → Plane size → Matrix per floor |
| Beam layout | Line, List Repeat, Matrix Apply | Grid lines → Profile sweep → Place |
| Facade panels | Box/Plane, Subdivide, Inset, List processing | Wall → Subdivide → Inset per cell → Gap |
| Curtain wall mullions | Line, Array Modifier, Matrix | Vertical/horizontal lines → Array → Join |
| Louver array | Plane, Matrix Rotation, List Repeat | Panel → Rotate → Array along facade |
| Straight stairs | Box, Number Range, Matrix offset | Tread box → Offset per step via Matrix |
| Spiral stairs | SNLite (trig), Box, Matrix rotation | Angle per step → Tread → Rotate + lift |
| Gable roof | Extrude edge, Move vertices | Footprint top edge → Extrude → Raise ridge |
| Hip roof | Inset Polygon, Move center up | Footprint → Inset → Raise center polygon |
| Pipe routing | Polyline, Circle profile, Sweep | Path points → Polyline → Sweep circle |
| Duct routing | Polyline, Rectangle profile, Sweep | Path points → Polyline → Sweep rectangle |
| Terrain mesh | CSV In, Delaunay 2D or Plane grid | Points → Triangulate or Grid → Set Z |
Matrix Transform Cheat Sheet
from mathutils import Matrix, Vector, Euler
import math
mat_loc = Matrix.Translation(Vector((x, y, z)))
mat_rot_z = Matrix.Rotation(math.radians(angle), 4, 'Z')
transform = mat_loc @ mat_rot_z
floor_offset = Matrix.Translation(Vector((0, 0, floor_index * floor_height)))
element_transform = floor_offset @ mat_loc @ mat_rot_z
mat_scale = Matrix.Diagonal(Vector((sx, sy, sz, 1.0)))
Data Input Methods
| Source | Sverchok Node | Output | Use Case |
|---|
| CSV file | SvCSVInNode | Strings/Numbers socket | Column positions, panel sizes, elevation data |
| JSON file | SvJsonInNode | Dictionary socket | Complex element definitions, BIM data |
| Blender object | SvGetObjectsData | Vertices/Edges/Faces | Site boundary, existing geometry |
| Manual entry | SvListInputNode | Strings socket | Small parameter sets |
| Number range | SvGenNumberRange | Strings socket | Regular grid spacing |
NumPy Acceleration for Large AEC Models
"""
in count s default=100
in spacing s default=6.0
out verts v
out faces s
"""
import numpy as np
n = int(count)
s = spacing
ix = np.arange(n)
iy = np.arange(n)
gx, gy = np.meshgrid(ix * s, iy * s)
gz = np.zeros_like(gx)
v = np.column_stack([gx.ravel(), gy.ravel(), gz.ravel()])
faces_list = []
for i in range(n - 1):
for j in range(n - 1):
idx = i * n + j
faces_list.append((idx, idx + 1, idx + n + 1, idx + n))
verts = [v.tolist()]
faces = [faces_list]
Blender Object Integration
import bpy, bmesh
def sv_output_to_blender(verts_nested, faces_nested, name_prefix="AEC"):
"""Convert Sverchok nested output to Blender mesh objects.
Parameters:
verts_nested: [[verts_obj0], [verts_obj1], ...] — Sverchok format
faces_nested: [[faces_obj0], [faces_obj1], ...] — Sverchok format
name_prefix: str — prefix for object names
"""
for idx, (v_list, f_list) in enumerate(zip(verts_nested, faces_nested)):
name = f"{name_prefix}_{idx:04d}"
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(v_list, [], f_list)
mesh.update()
if name in bpy.data.objects:
bpy.data.objects[name].data = mesh
else:
obj = bpy.data.objects.new(name, mesh)
bpy.context.collection.objects.link(obj)
Reference Links
Cross-References
- sverchok-syntax-scripting — SNLite node syntax, header format,
vectorize utility, setup() for pre-computation
- sverchok-core-concepts — Node tree architecture, update system, socket data cache, data nesting convention
- blender-core-api —
bpy.data access for meshes, objects, collections
- blender-core-runtime —
mathutils.Matrix, mathutils.Vector, mathutils.Euler for transforms
Official Sources