
This week, we bridge the gap between simple geometric transformations and complex iterative systems. We start by reviewing the logic behind Assignment 01 (Brep Meshing & RF Systems). We then explore the concept of Attractors- first as a tool for local geometric scaling, and then as a dynamic “force” within our mesh relaxation system. Finally, we will refactor our procedural code into an Object-Oriented structure using “Modifier” classes, setting the stage for the modular relaxer in Assignment A02.
We will discuss the solutions for the Brep Mesher and the Reciprocal Frame (RF) system.
compas_timber beams and joints.Before using attractors in complex simulations, we look at the simplest case: Distance-based Scaling.
compas.geometry to transform geometry relative to its proximity to an influence source.
Building on last week’s Mesh Relaxation, we introduce a new type of influence: the Attractor Force. Instead of just internal edge forces, we add an external vector that pulls mesh vertices toward a point (or pushes away from said point). This allows for “sculpting” the relaxed form by placing attractors in space.
Mesh relaxation is an iterative process used to find an equilibrium state for a network of vertices and edges. It can be used in computational design for form-finding and optimization among others.

The relaxation process is based on the interaction of internal and external forces acting on each vertex of the mesh.
Iterative Solvers: Instead of calculating the final shape in one step, we move vertices incrementally. In each “step” or “iteration,” we calculate the net force on a vertex and update its position.
0.95) to the movement, slowing down the vertices over time.In our code, a MeshRelaxer object manages the simulation. The core logic of a single relaxation step can be summarized in three main actions:
# The Core Logic of a Relaxation Step
for _ in range(self.iterations):
# 1. Calculate Forces (Internal + External)
# Each vertex calculates a vector pulling it toward its neighbors or attractors
for vertex in self.mesh.vertices():
force = calculate_spring_forces(vertex) + calculate_attractor_forces(vertex)
self.mesh.vertex_attribute(vertex, "force", force)
# 2. Update Positions
# Move vertices based on their accumulated forces
for vertex in self.mesh.vertices():
if not vertex_is_fixed:
force = self.mesh.vertex_attribute(vertex, "force")
new_pos = current_pos + force * damping
self.mesh.vertex_attributes(vertex, "xyz", list(new_pos))
# 3. Constrain to Surface (Optional)
# Pull the updated positions back onto a target Brep or Mesh
if self.snap_to_surface:
project_vertices_to_surface()
To make our relaxation system more modular, we move away from large, monolithic functions. We will implement Modifier Classes. We will create two main types of modifiers:
mesh_modifier: Changes the topology or fixed state of the mesh (e.g., SnapVertexToPoint, MergeFaces).force_modifier: Adds vectors to the vertices’ force attribute (e.g., AttractorPointModifier, DirectionalForce).This architectural shift allows us to “plug and play” different behaviors without changing the core solver.
Introduction to the next assignment, where you will implement these modifiers to create a sophisticated mesh relaxation tool.
↑ click to open ↑
The examples for this week can be found in the lectures/week-04/examples directory: