Skip to main content

Python CLI for converting a 3D model to LDraw.

Project description

legolization

Turn a colored voxel model into a physically buildable LEGO model in LDraw format, with step-by-step build instructions and a bill of materials.

This is the classic "LEGO construction problem" from the research literature (see references/): voxelize → hollow → place bricks → check structural stability → repair → export. The stability check is a full Rigid-Block-Equilibrium (RBE) model (StableLego formulation, cross-validated against its released test fixtures): per-brick force and torque balance with knob-friction capacities, solved as a provably exact linear program on an open solver stack — no Gurobi required.

What it does

  • Input: a MagicaVoxel .vox file or a numpy .npy array (LDraw colour codes or RGB(A) voxels — colours are quantized to the nearest solid LDraw colour, with optional Floyd-Steinberg dithering for gradients).
  • Placement: covers every voxel with bricks and plates at true heights (plate = 8 LDU, brick = 24 LDU) using one of six strategies; tiles and 45° slopes are opt-in finishing passes (--tiles, --slopes — the catalogued 33° slope and 2x2 slope are not yet placed by any pass):
    • greedy (default): largest-first bottom-up fill with Kollsker's remainder-lookahead h(r) and distance-decayed stretcher-bond scoring, then delete-and-rebuild reinforcement around the weakest bricks.
    • luo: Luo et al. (2015) maximal random merge with split-and-remerge refinement, accepted by Luo's maximin friction capacity C_M; supports soft colour constraints (--colour soft).
    • bond: Kollsker & Malaguti's constructive brick-bonding heuristic — remainder lookahead + staggering reward + per-layer repair.
    • fast: Bao et al.'s greedy per-layer merge with a dominant big-brick weight, perpendicularity term, and connectivity retries.
    • smga: Lee et al.'s split-and-merge genetic algorithm per layer (--ga-generations, --time-budget).
    • beauty: Min et al.'s objective-driven tiling with symmetry/balance, stability-priority, and big-brick terms (--beauty-preset {balanced,stability,aesthetics,efficiency}).
  • Physics: every layout is scored by the RBE — gravity, support, press, drag/pull friction (capacity T = 0.98 N per contact point), knob presses, and torque-capable side presses at shared-face extremes (side-supported structures shed load like Luo's bridges). Equilibrium residuals sit in the objective, so even collapsing structures solve and failures localize to specific bricks.
  • Repair: unstable layouts go through an ALNS destroy-and-repair pass (Kollsker's artificial-link QP pinpoints the deficit; the freed region is refilled by the merge engine or an exact-cover MILP) before any material is added back by the stability-aware hollow-restore loop.
  • Auto-hollow: interiors are hollowed to a shell (~1 brick thick, tunable with --shell-plates); interior cells are colour-free so merges never fragment on invisible boundaries.
  • Instructions: smart step sequencing (default) chunks each layer into ~7-brick spatially coherent steps, keeps mirror-symmetric halves together, guarantees every prefix is stable and vertically insertable (or warns), and adds 0 ROTSTEP view hints. --bom out.json writes a bill of materials with per-step callouts.
  • Output: a valid .ldr or .mpd written through pyldraw3. Open it in LDView or BrickLink Studio.

Setup

uv sync
uv run ldraw download   # once: fetch the LDraw parts library
uv run ldraw generate   # once: generate ldraw.library.* part/colour modules

Usage

uv run legolization data/examples/heart.vox -o heart.ldr
uv run legolization model.npy --strategy beauty --beauty-preset aesthetics
uv run legolization model.vox --strategy bond --bom parts.json
uv run legolization model.npy --strategy luo --solid --seed 7
uv run legolization model.vox --slopes --tiles      # surface finishing passes
uv run legolization model.vox --aspect-correct      # keep cubic voxel aspect
uv run legolization model.vox --milp                # cross-check the exact LP

The CLI reports brick count, mass, step count, and the physics verdict:

wrote heart.ldr
  bricks: 12   mass: 17.7 g   steps: 7   slopes: 0   tiles: 0
  stability: STABLE (worst score 0.001, min capacity 0.979 N)

Exit code 0 means the model is stable, one stud-connected component, and ground-connected. Exit 2 means it is not buildable as-is (try another --strategy, --solid, or a different --seed) — note that an input made of several disconnected voxel islands is reported as multiple components even when every island stands on the ground.

Python API:

from pathlib import Path
from legolization import PipelineConfig, VoxelGrid, run, run_file

result = run_file(Path("model.vox"), Path("model.ldr"), PipelineConfig(seed=1))
print(result.buildable, result.step_count, result.stability.max_score)

How the stability model works

Each mated stud contributes 3 or 4 contact points (per StableLego's measured geometry) carrying a shared normal force and a friction (drag/pull) force, so Newton's third law holds by construction; each knob adds four horizontal knob-press forces, and laterally touching bricks exchange two side presses at the shared face's vertical extremes so lateral load transfer carries torque. Per brick, five equilibrium residuals (3 forces, 2 torques about the mass centroid) are minimized rather than constrained. A brick scores 1 when it cannot reach equilibrium or its friction demand exceeds T; otherwise drag_max / T — so the score doubles as a stress heatmap. The default solver is a hand-assembled LP on scipy/HiGHS, and the relaxation is provably exact (each contact's press and pull columns are exact negatives, so no optimum ever uses both); --milp re-verifies with explicit big-M complementarity via cvxpy. The whole stack reproduces all nine verdicts of the StableLego release's test fixtures (vendored under tests/data/stablelego/).

Benchmark

uv run python scripts/benchmark.py compares all six strategies across the example models (brick count, stability margin, seam/perpendicularity/symmetry metrics, runtime). Highlights at seed 0: bond and beauty cover the arch in 13 bricks where largest-first greedy needs 32, and beauty --beauty-preset aesthetics produces perfectly mirror-symmetric layers on symmetric models.

Development

uv run pytest          # analytic physics, placement invariants, golden pins
uv run ruff format --check . && uv run ruff check .
uv run ty check src tests
uv run pyrefly check src tests

License

GPL-3.0-or-later (inherited from pyldraw3).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

legolization-0.2.0.tar.gz (72.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

legolization-0.2.0-py3-none-any.whl (93.5 kB view details)

Uploaded Python 3

File details

Details for the file legolization-0.2.0.tar.gz.

File metadata

  • Download URL: legolization-0.2.0.tar.gz
  • Upload date:
  • Size: 72.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for legolization-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e61f0630a656d19345d37cde19be665193deff160360e24ebb201f9d77a8fdce
MD5 5dd722483429a7fe8a3478ab8ddc0e7d
BLAKE2b-256 d825ae9409f3bf892fa242cc83e13c5f57b8d7bcbc85e5d401e61fcc28e2b314

See more details on using hashes here.

Provenance

The following attestation bundles were made for legolization-0.2.0.tar.gz:

Publisher: publish.yml on hbmartin/legolization

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file legolization-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: legolization-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 93.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for legolization-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2e4efba22a3319a5e1bb8862651f68327fe46a0a245c37ba50e2dec3fe495343
MD5 fbfee34d06c22e85ab22d2dbf167e0d6
BLAKE2b-256 ad47798262e14dc25e270c48e5ca9b708a2bb521bdc97f5425de1bab8ae5c3f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for legolization-0.2.0-py3-none-any.whl:

Publisher: publish.yml on hbmartin/legolization

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page