Mesh-to-CAD 14B

Successor: noahclark556/mesh-to-cad-14b-v2 is the current recommended checkpoint (corrected six-run SFT stack merged with the fixed ORPO train). This v1 page stays up as the earlier public baseline.

An experimental model that turns an STL mesh or point cloud into editable CadQuery Python.

The goal is a program you can read and change, not just another mesh that looks similar. Multiple scripts can describe the same shape, so exact source recovery is not the bar.

License: CC BY-NC 4.0. Non-commercial use with attribution is welcome. Commercial use needs a separate license.

This is not a normal text chat model. Geometry is sampled, encoded, and passed through a point projector into inputs_embeds. The language model then writes CadQuery after a <|gen|> boundary token.

Why I built this

My initial reason for building this model was to use it within my web platform SliceFoundry (SliceFoundry.com), as well as within my desktop application Draftr (draftrcad.com). A lot of users come to these platforms trying to "remix" an existing STL, which is a supported feature in both applications. However, the system currently in place hits a wall that cannot be crossed without a custom trained model. The idea moving forward, which is currently working well with model versions that have not yet been released, is to have the mesh-to-cad model produce the geometry from the uploaded mesh, then run that geometry through the existing parametric fine-tune pipeline alongside a detailed analysis of the mesh.

This system has proven to be far more accurate than I initially anticipated, and should save a lot of time and spending for me as development continues. The process of training this model has been a relatively large (but informative) undertaking, as the training setup had to be almost entirely custom to support a project built in this way. Over the course of multiple months, I have built a massive and streamlined local pipeline for this training process.

From dataset generation modules to training and deployment scripts for both GCP and RunPod, as well as full support for training the model entirely locally. A lot of the training that occurred was done locally, and I bought a new MacBook partly so I could do this. Over the course of 12 SFT sessions, 4-5 GRPO sessions, 16 ORPO sessions, and an absurdly large amount of training data built using data extracted from the aforementioned platforms and synthetically produced, mesh-to-cad was born through the forge of trial and error.

Try it (Apple Silicon / MLX)

huggingface-cli download noahclark556/mesh-to-cad-14b --local-dir ./mesh-to-cad-14b
cd mesh-to-cad-14b
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python chat.py --input /path/to/part.stl
python chat.py --input /path/to/part.stl --render
Flag Default Notes
--model . Folder with weights + projector.pt
--temp 0.2 Lower is more deterministic
--top-k 0 (off)
--top-p 0.0 (off)
--max-tokens 2048 Raise if long scripts get cut off
--repetition-penalty 1.08 Helps avoid loops
--render off Exec CadQuery, write STL, open it

A few things happen automatically so the path matches training:

  • Input is resized so the longest bbox edge is 100.
  • mesh_sig is derived from the mesh when possible and inserted in the prefix (points + sig + <|gen|>).
  • Dense sampling uses fine tessellation and feature-aware points, then split FPS down to 1024 tokens.

--render runs the generated script, writes infer_out/<stem>.stl, and opens it. If CadQuery fails, the script errors after a light local cleanup (trimming an incomplete trailing line). There is no external repair API.

CadQuery is recommended for sampling that matches training, and required for --render:

mamba install -c conda-forge cadquery

Use conda-forge / OCP. Avoid pip-only CadQuery on Mac.

MLX and other stacks

chat.py is written for Apple Silicon via MLX / mlx-lm. That is the supported easy path.

The weights themselves are standard Transformers safetensors plus projector.pt, so other runtimes can load them. You will need to rebuild the geometry prefix yourself (Fourier features -> projector -> inputs_embeds). Plain generate(), llama.cpp, and text-only servers will not see the point cloud and will not work for this model as-is.

Pipeline

STL / point cloud
  -> fine tessellation
  -> 8192 feature-aware surface samples
  -> split FPS (768 global + 256 feature) = 1024 tokens
  -> 12-frequency Fourier encoding (75D) + linear projector
  -> optional mesh_sig tokens (compact geometry summary)
  -> <|gen|>
  -> CadQuery Python

The model does not see a chat-style system prompt. Conditioning is the prefix itself: projected point tokens, then (when available) a short mesh_sig string embedded as normal text tokens, then <|gen|>.

How this model was built

Early versions used a single FPS pass at 512 tokens on coarser surface samples. That was enough for simple blocks, but pockets, holes, and thin local detail tended to disappear before the model ever saw them.

What made a real difference:

  1. Finer tessellation when exporting STL for sampling, so edges and small faces survive into the point cloud.
  2. Feature-aware dense sampling (8192 points), so sharp edges and small faces are not starved by area weighting alone.
  3. Split FPS to 1024 tokens: most of the budget for global coverage (768), with a reserved feature pool (256) so local detail still gets tokens. Same count as a plain 1024 FPS, better retention of pockets/holes.
  4. Mesh signature (mesh_sig) - a compact discrete summary of the part (family / topology-ish cues in a short SIG v1 ... string). It sits in the prefix between the point embeddings and <|gen|>, so the model gets an explicit high-level read of the shape before it starts writing code. In practice it plays a similar role to a system prompt (steering the generation with structured context), but it is geometry conditioning in the input prefix, not a chat system message. Adding it helped a lot on harder parts where points alone were ambiguous.
  5. SFT on Qwen3-14B-Base with a Fourier point projector and a dedicated <|gen|> generation boundary.
  6. Additional ORPO preference passes to sharpen CadQuery quality on harder cases.

The training corpus is not released with this checkpoint. Provenance and licensing for the data are still private; this repo is the model and the inference helpers only.

What's in the repo

  • model*.safetensors - language model weights
  • projector.pt - point-cloud -> embedding projector
  • tokenizer / config (includes <|gen|>)
  • chat.py + mesh_to_cad/ - MLX test path
  • requirements.txt

Limits

This is still research software. Generated Python can fail to run or produce empty / invalid solids. Even when geometry looks close, the construction history may be very different from the original CAD.

Thin walls, hidden cavities, threads, assemblies, organic surfaces, and tiny features are difficult from exterior samples alone. Small holes, pockets, and fillets are often simplified or dropped. Absolute scale is normalized unless your own pipeline restores it.

Citation

If you use Mesh-to-CAD, please credit the project and link this model page. For commercial licensing, contact me.

Continuation

If you find this model interesting or want to see more in the future, drop a like so I know there is some interest in the project. Thanks!

Prior work

The point-cloud soft-token idea (sample points, Fourier-encode them, project into the LM embedding space, then generate CAD code) is not mine. I first saw that setup in:

Credit to those authors for that geometry-prefix approach. Everything else here (data, training stack, mesh signatures, split FPS, CadQuery target, ORPO, etc.) is my own work. Citing them so nobody thinks I invented the soft-token pattern.

Downloads last month
157
Safetensors
Model size
15B params
Tensor type
U32
ยท
F16
ยท
MLX
Hardware compatibility
Log In to add your hardware

6-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for noahclark556/mesh-to-cad-14b

Quantized
(28)
this model