yilinliu77/Brepler

Python

11

1 commits

updated Jul 3, 2026

See the code

README

B-repLer: Language-guided Editing of CAD Models

This is the official implementation of B-repLer: Language-guided Editing of CAD Models (arXiv:2508.10201), accepted to SIGGRAPH 2026.

B-repLer edits boundary-representation (B-rep) CAD models directly from natural-language instructions. Rather than relying on construction history — a major limitation of prior work — it operates in a learned latent space, letting it handle everything from simple prismatic parts to complex B-Spline surfaces while keeping the output a valid B-rep. To train the framework we introduce BrepEDIT-240K, a large-scale dataset of paired edits built automatically with existing CAD tools and multimodal language models.

This repository shows the general input and output of the B-rep editing pipeline, and how to test on a single B-rep and prompt, then batch process the full testset. It also shows how to use the pretrained HoLa VAE encoder-decoder to get the latent representation and decoded face and edge predictions, and how to run the post-processing and B-rep reconstruction on those predictions.

Installation

Environment

B-repLer relies on pythonOCC for B-rep I/O, which is only available through conda:

conda create -n brepler python=3.10
conda activate brepler
conda install conda-forge::pythonocc-core==7.8.1

The remaining dependencies (PyTorch for CUDA 12.4, PyTorch Geometric, and the core packages) are pinned in requirements.txt — the file already includes the extra PyTorch/PyG index URLs, so a single command installs everything:

pip install -r requirements.txt

Checkpoints and data

The pretrained checkpoints and datasets are hosted online. Download and unpack them as follows:

# --- pretrained checkpoints ---
wget "https://www.dropbox.com/scl/fi/38klqe5hdomc6t9xbiv84/brepler_ckpt.zip?rlkey=ebo0oabf6p1zkxnt0eamkj976&dl=1" -O brepler_ckpt.zip
unzip brepler_ckpt.zip -d ckpt/                # -> ckpt/brepler_editor.ckpt, ckpt/hola_abc_vae.ckpt (+ brepler_editor2.ckpt, the earlier 550k-step variant)

# --- BrepEDIT-240K processed training / validation / testing data ---
wget "https://www.dropbox.com/scl/fi/8qt05e2beaxdzkm220jty/brepler_training.zip?rlkey=akfbk0ysu3ydqz2xrxxc4sinf&dl=1" -O brepler_training.zip
unzip brepler_training.zip -d data/            # -> data/brepler_training

# --- raw CAD source data (only needed to regenerate the dataset from scratch) ---
wget "https://www.dropbox.com/scl/fi/dy59w7io50hmrn3hoaeh4/brepler_raw.zip?rlkey=tu5dppd8mx5qz70y2eiqv3uq7&dl=1" -O brepler_raw.zip
unzip brepler_raw.zip -d data/                 # -> data/brepler_raw

The commands below take these locations as arguments, e.g. --ckpt ckpt/brepler_editor.ckpt, model.hola_weights=ckpt/hola_abc_vae.ckpt, and dataset.data_root=data/brepler_training.

B-repler Usage

Test on a single B-rep and prompt

Input: a single B-rep as a STEP file, plus a natural-language edit instruction.

Output: the editor samples several candidate edits of the input (one per orientation augmentation — 24 by default), reconstructs each back into a valid B-rep, and writes everything under the output folder:

  • rendered.png / proj_matrix.txt — the rendering of the input B-rep used as image conditioning.
  • inter/post_<i>/data.npz (+ rotation.npy) — the raw per-candidate face/edge/adjacency predictions.
  • inter/post_<i>/pp/recon_brep.step (+ .ply) — the reconstructed edited B-rep for candidate i; pp/success.txt marks a valid watertight solid.
  • result_grid.png — the input (blue border) next to every candidate output, each labeled with its id and SUCCESS/FAIL.

A summary (number of valid solids and where the outputs live) is printed at the end.

python -m single_inference --step /path/to/start.step --prompt "Add an internal cave to the model" --output outputs/edit

With no arguments it runs the bundled example (examples/examples1/start.step, prompt "Add an internal cave to the model").

Reproducing the paper metrics (quick check)

The main-paper numbers are computed over the full B-repLer test set (../lists/brepler_test.txt, ~10k cases). For a fast sanity check that runs the exact same pipeline, the commands below evaluate the released checkpoint on a 500-example subset (../lists/brepler_testing_500.txt, the default dataset.test_dataset) and report the two headline metrics — validity and Chamfer distance. Point dataset.data_root at the downloaded brepler_training folder:

python -m train trainer.gpu=8 trainer.accelerator=16-mixed dataset.data_root=/path/to/brepler_training dataset.num_test_trials=8 trainer.test_output_dir=test/my_run trainer.resume_from_checkpoint=/path/to/brepler_editor.ckpt model.hola_weights=/path/to/hola_abc_vae.ckpt

The model and dataset default to the released architecture (model.name=BreplerEditor, dataset.name=EditDataset in train.yaml), matching the released checkpoint — no need to set them on the command line. Each of the 500 shapes is sampled num_test_trials (8) times, and the raw per-trial predictions are written to test/my_run/<shape>_<trial>/data.npz. Swap in dataset.test_dataset=../lists/brepler_test.txt for the full test set, or ../lists/brepler_easy.txt / brepler_moderate.txt / brepler_difficult.txt for the per-difficulty breakdown (difficulty = edit magnitude).

Validity. Reconstruct every prediction into a B-rep and score:

python -m post_processing test/my_run

This writes <shape>_<trial>/pp/recon_brep.step (with pp/success.txt for watertight solids), copies one successful trial per shape into solution_<shape>/, and prints the per-shape validity — a shape is valid if any of its trials yields a valid solid. Pass --no_pack to skip the copy step. (pack_cases.py test/my_run re-reports the same validity without re-running reconstruction.)

Chamfer distance. Against the ground-truth edited B-reps (/path/to/brepler_raw, laid out as <shape>/<op>/before.step|after.step):

python eval_cd.py test/my_run /path/to/brepler_raw

On the released checkpoint this quick check gives validity ≈ 91% (457/500) and median Chamfer distance ≈ 0.05 over the valid reconstructions (the mean, ≈0.16, is skewed by a small tail of failed edits — eval_cd.py reports the median), in line with the full-testset numbers reported in the paper.

HoLa VAE

HoLa VAE is the geometry autoencoder behind HoLa: B-Rep Generation using a Holistic Latent Representation (arXiv:2504.14257). It encodes a boundary-representation (B-rep) solid into a single holistic latent space that stores only per-face surface geometry — curves, vertices, and every topological connection are dropped from the latent and instead re-derived at decode time by a neural intersection network that distinguishes and reconstructs each edge from a pair of neighbouring surfaces. It is the encoder-decoder that produces the latent HoLa's generator operates in; here it is exposed standalone so you can encode a B-rep to its latent and decode a latent back to a B-rep.

Input and output

Input. A STEP file holding a single solid B-rep, subject to two constraints: the solid must be a single connected component, and any two faces may share at most one intersecting edge.

Latent. The encoder emits one latent vector per face, so the latent has shape (num_faces, 32): a variable number of rows — one per face — each of dimension 32 (dim_latent = 8, expanded to 8 × 2 × 2 = 32). There is no separate edge or topology latent; the 32-d per-face codes are the entire representation. Row order follows the face ordering in sequence_order.

The HoLa VAE entrypoints live in release/network/hola.py:

  • initialize_hola_model(checkpoint_path) loads the pretrained HoLa encoder-decoder on CUDA.
  • prepare_hola_data(step_file, projection=None) converts a STEP solid into the sampled face and edge representation expected by the VAE.
  • encode_hola_latent(model, step_file, projection=None) returns the latent array with shape (num_faces, 32) and the prepared HoLa input dictionary.
  • decode_hola_latent(model, latent) returns the raw decoded arrays in memory.
  • reconstruct_hola_latent(model, latent, output_root, prefix) saves latent.npy, writes data.npz, and reconstructs the B-rep under output_root / prefix.

Important constraints:

  • The STEP input must contain exactly one connected component.
  • Any two faces may share at most one intersecting edge; each face pair is encoded as a single (possibly merged) edge.
  • The helper code assumes CUDA is available. The model and intermediate tensors are moved to GPU inside the helper functions.
  • prepare_hola_data(...) uses the non-augmented single-shape path. Augmentation remains only for dataset generation.
  • The latent rows follow the face ordering produced by prepare_hola_data, stored in sequence_order.

If you only want the raw decoded arrays instead of a reconstructed B-rep folder, call model.inference(...) directly on a CUDA tensor. That returns:

  • face_features
  • pred_face_adj
  • pred_face_adj_prob
  • pred_edge_face_connectivity
  • pred_face
  • pred_edge

Encode and decode a single B-rep

Example:

from pathlib import Path

import numpy as np

from hola import (
	encode_hola_latent,
	initialize_hola_model,
	reconstruct_hola_latent,
)

checkpoint = Path("/path/to/hola_abc_vae.ckpt")
step_file = Path("/path/to/model.step")
output_dir = Path("outputs/hola")
output_dir.mkdir(parents=True, exist_ok=True)

model = initialize_hola_model(str(checkpoint))
latent, prepared = encode_hola_latent(model, step_file)
print(latent.shape)  # (num_faces, 32)

reconstruct_hola_latent(model, latent, output_dir, prefix=step_file.stem)

After this:

  • output_dir / step_file.stem / latent.npy contains the encoded HoLa latent.
  • output_dir / step_file.stem / data.npz contains the decoded face and edge prediction.
  • output_dir / step_file.stem / pp contains the post-processing outputs.
  • output_dir / step_file.stem / recon_brep.step and recon_brep.ply are copied up for convenience.

There is also a CLI example in the same module. Run it from release/network/ (the module uses top-level imports like post and occ_utils):

python -m hola --checkpoint /path/to/hola_abc_vae.ckpt --step /path/to/model.step --output outputs/hola

If you want the decoder outputs without writing the reconstructed CAD files:

import torch

latent_tensor = torch.from_numpy(latent).cuda()
with torch.no_grad():
	decoded = model.inference(latent_tensor)

decoded["pred_face_adj"] = decoded["pred_face_adj"].cpu().numpy()

This path is useful if you want to inspect predicted faces, edges, or adjacency before running B-rep construction.

Data pipeline

Refer to data.md

yilinliu77/Brepler

Python

11

1 commits

updated Jul 3, 2026

See the code

README

B-repLer: Language-guided Editing of CAD Models

This is the official implementation of B-repLer: Language-guided Editing of CAD Models (arXiv:2508.10201), accepted to SIGGRAPH 2026.

B-repLer edits boundary-representation (B-rep) CAD models directly from natural-language instructions. Rather than relying on construction history — a major limitation of prior work — it operates in a learned latent space, letting it handle everything from simple prismatic parts to complex B-Spline surfaces while keeping the output a valid B-rep. To train the framework we introduce BrepEDIT-240K, a large-scale dataset of paired edits built automatically with existing CAD tools and multimodal language models.

This repository shows the general input and output of the B-rep editing pipeline, and how to test on a single B-rep and prompt, then batch process the full testset. It also shows how to use the pretrained HoLa VAE encoder-decoder to get the latent representation and decoded face and edge predictions, and how to run the post-processing and B-rep reconstruction on those predictions.

Installation

Environment

B-repLer relies on pythonOCC for B-rep I/O, which is only available through conda:

conda create -n brepler python=3.10
conda activate brepler
conda install conda-forge::pythonocc-core==7.8.1

The remaining dependencies (PyTorch for CUDA 12.4, PyTorch Geometric, and the core packages) are pinned in requirements.txt — the file already includes the extra PyTorch/PyG index URLs, so a single command installs everything:

pip install -r requirements.txt

Checkpoints and data

The pretrained checkpoints and datasets are hosted online. Download and unpack them as follows:

# --- pretrained checkpoints ---
wget "https://www.dropbox.com/scl/fi/38klqe5hdomc6t9xbiv84/brepler_ckpt.zip?rlkey=ebo0oabf6p1zkxnt0eamkj976&dl=1" -O brepler_ckpt.zip
unzip brepler_ckpt.zip -d ckpt/                # -> ckpt/brepler_editor.ckpt, ckpt/hola_abc_vae.ckpt (+ brepler_editor2.ckpt, the earlier 550k-step variant)

# --- BrepEDIT-240K processed training / validation / testing data ---
wget "https://www.dropbox.com/scl/fi/8qt05e2beaxdzkm220jty/brepler_training.zip?rlkey=akfbk0ysu3ydqz2xrxxc4sinf&dl=1" -O brepler_training.zip
unzip brepler_training.zip -d data/            # -> data/brepler_training

# --- raw CAD source data (only needed to regenerate the dataset from scratch) ---
wget "https://www.dropbox.com/scl/fi/dy59w7io50hmrn3hoaeh4/brepler_raw.zip?rlkey=tu5dppd8mx5qz70y2eiqv3uq7&dl=1" -O brepler_raw.zip
unzip brepler_raw.zip -d data/                 # -> data/brepler_raw

The commands below take these locations as arguments, e.g. --ckpt ckpt/brepler_editor.ckpt, model.hola_weights=ckpt/hola_abc_vae.ckpt, and dataset.data_root=data/brepler_training.

B-repler Usage

Test on a single B-rep and prompt

Input: a single B-rep as a STEP file, plus a natural-language edit instruction.

Output: the editor samples several candidate edits of the input (one per orientation augmentation — 24 by default), reconstructs each back into a valid B-rep, and writes everything under the output folder:

  • rendered.png / proj_matrix.txt — the rendering of the input B-rep used as image conditioning.
  • inter/post_<i>/data.npz (+ rotation.npy) — the raw per-candidate face/edge/adjacency predictions.
  • inter/post_<i>/pp/recon_brep.step (+ .ply) — the reconstructed edited B-rep for candidate i; pp/success.txt marks a valid watertight solid.
  • result_grid.png — the input (blue border) next to every candidate output, each labeled with its id and SUCCESS/FAIL.

A summary (number of valid solids and where the outputs live) is printed at the end.

python -m single_inference --step /path/to/start.step --prompt "Add an internal cave to the model" --output outputs/edit

With no arguments it runs the bundled example (examples/examples1/start.step, prompt "Add an internal cave to the model").

Reproducing the paper metrics (quick check)

The main-paper numbers are computed over the full B-repLer test set (../lists/brepler_test.txt, ~10k cases). For a fast sanity check that runs the exact same pipeline, the commands below evaluate the released checkpoint on a 500-example subset (../lists/brepler_testing_500.txt, the default dataset.test_dataset) and report the two headline metrics — validity and Chamfer distance. Point dataset.data_root at the downloaded brepler_training folder:

python -m train trainer.gpu=8 trainer.accelerator=16-mixed dataset.data_root=/path/to/brepler_training dataset.num_test_trials=8 trainer.test_output_dir=test/my_run trainer.resume_from_checkpoint=/path/to/brepler_editor.ckpt model.hola_weights=/path/to/hola_abc_vae.ckpt

The model and dataset default to the released architecture (model.name=BreplerEditor, dataset.name=EditDataset in train.yaml), matching the released checkpoint — no need to set them on the command line. Each of the 500 shapes is sampled num_test_trials (8) times, and the raw per-trial predictions are written to test/my_run/<shape>_<trial>/data.npz. Swap in dataset.test_dataset=../lists/brepler_test.txt for the full test set, or ../lists/brepler_easy.txt / brepler_moderate.txt / brepler_difficult.txt for the per-difficulty breakdown (difficulty = edit magnitude).

Validity. Reconstruct every prediction into a B-rep and score:

python -m post_processing test/my_run

This writes <shape>_<trial>/pp/recon_brep.step (with pp/success.txt for watertight solids), copies one successful trial per shape into solution_<shape>/, and prints the per-shape validity — a shape is valid if any of its trials yields a valid solid. Pass --no_pack to skip the copy step. (pack_cases.py test/my_run re-reports the same validity without re-running reconstruction.)

Chamfer distance. Against the ground-truth edited B-reps (/path/to/brepler_raw, laid out as <shape>/<op>/before.step|after.step):

python eval_cd.py test/my_run /path/to/brepler_raw

On the released checkpoint this quick check gives validity ≈ 91% (457/500) and median Chamfer distance ≈ 0.05 over the valid reconstructions (the mean, ≈0.16, is skewed by a small tail of failed edits — eval_cd.py reports the median), in line with the full-testset numbers reported in the paper.

HoLa VAE

HoLa VAE is the geometry autoencoder behind HoLa: B-Rep Generation using a Holistic Latent Representation (arXiv:2504.14257). It encodes a boundary-representation (B-rep) solid into a single holistic latent space that stores only per-face surface geometry — curves, vertices, and every topological connection are dropped from the latent and instead re-derived at decode time by a neural intersection network that distinguishes and reconstructs each edge from a pair of neighbouring surfaces. It is the encoder-decoder that produces the latent HoLa's generator operates in; here it is exposed standalone so you can encode a B-rep to its latent and decode a latent back to a B-rep.

Input and output

Input. A STEP file holding a single solid B-rep, subject to two constraints: the solid must be a single connected component, and any two faces may share at most one intersecting edge.

Latent. The encoder emits one latent vector per face, so the latent has shape (num_faces, 32): a variable number of rows — one per face — each of dimension 32 (dim_latent = 8, expanded to 8 × 2 × 2 = 32). There is no separate edge or topology latent; the 32-d per-face codes are the entire representation. Row order follows the face ordering in sequence_order.

The HoLa VAE entrypoints live in release/network/hola.py:

  • initialize_hola_model(checkpoint_path) loads the pretrained HoLa encoder-decoder on CUDA.
  • prepare_hola_data(step_file, projection=None) converts a STEP solid into the sampled face and edge representation expected by the VAE.
  • encode_hola_latent(model, step_file, projection=None) returns the latent array with shape (num_faces, 32) and the prepared HoLa input dictionary.
  • decode_hola_latent(model, latent) returns the raw decoded arrays in memory.
  • reconstruct_hola_latent(model, latent, output_root, prefix) saves latent.npy, writes data.npz, and reconstructs the B-rep under output_root / prefix.

Important constraints:

  • The STEP input must contain exactly one connected component.
  • Any two faces may share at most one intersecting edge; each face pair is encoded as a single (possibly merged) edge.
  • The helper code assumes CUDA is available. The model and intermediate tensors are moved to GPU inside the helper functions.
  • prepare_hola_data(...) uses the non-augmented single-shape path. Augmentation remains only for dataset generation.
  • The latent rows follow the face ordering produced by prepare_hola_data, stored in sequence_order.

If you only want the raw decoded arrays instead of a reconstructed B-rep folder, call model.inference(...) directly on a CUDA tensor. That returns:

  • face_features
  • pred_face_adj
  • pred_face_adj_prob
  • pred_edge_face_connectivity
  • pred_face
  • pred_edge

Encode and decode a single B-rep

Example:

from pathlib import Path

import numpy as np

from hola import (
	encode_hola_latent,
	initialize_hola_model,
	reconstruct_hola_latent,
)

checkpoint = Path("/path/to/hola_abc_vae.ckpt")
step_file = Path("/path/to/model.step")
output_dir = Path("outputs/hola")
output_dir.mkdir(parents=True, exist_ok=True)

model = initialize_hola_model(str(checkpoint))
latent, prepared = encode_hola_latent(model, step_file)
print(latent.shape)  # (num_faces, 32)

reconstruct_hola_latent(model, latent, output_dir, prefix=step_file.stem)

After this:

  • output_dir / step_file.stem / latent.npy contains the encoded HoLa latent.
  • output_dir / step_file.stem / data.npz contains the decoded face and edge prediction.
  • output_dir / step_file.stem / pp contains the post-processing outputs.
  • output_dir / step_file.stem / recon_brep.step and recon_brep.ply are copied up for convenience.

There is also a CLI example in the same module. Run it from release/network/ (the module uses top-level imports like post and occ_utils):

python -m hola --checkpoint /path/to/hola_abc_vae.ckpt --step /path/to/model.step --output outputs/hola

If you want the decoder outputs without writing the reconstructed CAD files:

import torch

latent_tensor = torch.from_numpy(latent).cuda()
with torch.no_grad():
	decoded = model.inference(latent_tensor)

decoded["pred_face_adj"] = decoded["pred_face_adj"].cpu().numpy()

This path is useful if you want to inspect predicted faces, edges, or adjacency before running B-rep construction.

Data pipeline

Refer to data.md