Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

merka

2D parametric sketch editor. Rust + Bevy + egui.

Features

  • Draw segments, circles, arcs with snapping
  • Geometric constraint solver (GCS) — 15 constraint types
  • DOF analysis, redundancy detection
  • Undo/redo, soft-delete
  • Rectangle tool intentionally provides a central snap point; the visible construction centerline exists to make that center point work reliably and support midpoint workflow

Constraints

Constraint Selection
Horizontal segment(s)
Vertical segment(s)
Perpendicular ⊥ 2 segments
Parallel ∥ 2 segments
Tangent segment + circle/arc
Coincident 2+ points
Distance 2 points
Equal length = 2 segments
Angle ∠ 2 segments
Fix point(s)
Point on line point(s) + segment
Point on circle point(s) + circle
Midpoint point + segment
Symmetric 2 points + segment

Notes:

  • Dimension constraints stay point-based. We do not attach a dimension to a segment as a first-class reference, because a segment dimension is just the same relation between its two endpoint points.
  • This avoids duplicating the model and keeps solver/serialization/undo logic simpler. If we ever improve this area, it should be only as selection UX that expands a clicked segment into its endpoints, not as a new constraint representation.
  • Constraint model is canonical: Coincident is point-to-point only. Curve membership must use PointOnLine, PointOnCircle, or PointOnArc.
  • There is no separate store-level Collinear: two segments use Tangent, which the solver maps to internal collinearity.

Solver

Gauss-Newton GCS with forward-mode autodiff (Pwd / PwdVec). Newton step via nalgebra SVD — handles under/over/exactly-determined systems. DOF and redundancy computed via SVD rank.

See SOLVER.md for details and roadmap.

Build

cargo run

Requires a Wayland compositor. Key bindings: 1 line, 2 circle, 3 arc, Esc select, M move, Ctrl+Z/Y undo/redo.

See HOTKEYS.md for the full, maintained shortcut reference.

WASM

NO_COLOR=false trunk serve

If your shell exports NO_COLOR=1, trunk 0.21.x may fail to parse it; overriding with NO_COLOR=false keeps the web build working.

UI Layout

  • The sketch scene renders to the full canvas.
  • egui panels are treated as overlays on top of the scene instead of shrinking the scene viewport.
  • The top toolbar uses a fixed overlay height.
  • Scene input is blocked while the pointer is over egui via EguiBlocksInput.
  • We keep separate world and egui cameras so UI layout does not drag the scene camera with it.

Notes

  • Our local vendor/egui patch must only clamp interact_rect for panel hit-testing.
  • Do not clamp response.rect in top-level panels: egui uses it for subsequent panel allocation and shrinking it causes panel overlap/regressions.
  • Historical upstream context for mouse input in egui lives in two old enomado PRs: #1605 "Feature Mouse Lock API" explored cursor lock/grab for drag-style widgets, and #1614 "Mouse delta from DeviceEvent" explored feeding raw/native mouse motion separately from pointer position.
  • The useful takeaway from those threads is architectural: raw motion / native event handling belongs in the integration/backend layer, while core egui should receive documented, backend-agnostic input semantics.
  • Current de-vendoring direction: keep the egui patch as small as possible and upstream only the DragValue::use_raw_motion(true) style behavior (raw motion preferred over pointer delta, normalized by pixels_per_point, with a normal fallback).
  • bevy_egui should own the integration part: feed MouseMotion/raw device deltas into egui input, and translate ViewportCommand cursor requests into Bevy window cursor state.
  • merka should own cursor-lock policy: deciding when numeric drags hide/lock/unlock the cursor is app behavior and should stay in local state machines/systems, not inside vendored egui.
  • In other words, the path to removing vendored egui changes is: upstream raw-motion support to egui, upstream/backend the Bevy integration pieces to bevy_egui, then keep only merka's local UX policy on top.
  • Panel hit-testing note: the local vendor/egui panel fix is still active and still only clamps response.interact_rect. If scene input starts feeling blocked outside the visible panel again, first check for integration-level pointer absorption before suspecting panel.rs regressions; enabling bevy_egui's global absorb-input path can bypass our local rect-based scene gating.
  • Links: emilk/egui#1605 emilk/egui#1614

References

External projects we read for comparison. Per-primitive canonical-representation comparison against FreeCAD / LibreCAD / QCAD / SolveSpace lives in CANONICAL_GEOMETRY.md.

CatCad / silverpoint

https://github.com/xorza/CatCad — Rust parametric CAD (GPL-3.0-or-later, so it is a reading reference only, not a source of code). Its silverpoint crate is the closest thing to our sketch_core + mm_solver: 2D sketch geometry, constraints and solver in one library, with the app on top of its own GUI framework (palantir) and a wgpu renderer (aperture3d).

Where it lines up with us:

  • same shape of core — sketch arena with generational ids, constraints as residuals, DOF and redundancy from the rank of the Jacobian, undo as a whole-sketch snapshot;
  • drag is a pull, not a written position: the cursor target enters the solve as a weak least-squares term (their Pull, our WeakPointTarget), so geometry is never teleported off the constraint set and cleaned up afterwards;
  • Coincident is point-to-point only, curve membership is its own constraint (PointOnSegment / PointOnCircle) — the same anti-ambiguity rule we hold.

Where it is ahead of us:

  • profiles and 3D. arrangement cuts every curve at every crossing, walks the loops and reads signed area, so it answers "what regions does this drawing enclose" and feeds prism (extrude) and triangulation. Our enclave graph answers a different question — connected components for the solver and for selection — and we have no face extraction and no 3D at all.
  • feature history. A timeline of steps with sketches hanging off planes, i.e. actual parametric modelling; we have a flat sketch plus undo.
  • per-entity freedom. The solve reports Determined / Partly / Free per point, segment and circle out of the null space of the Jacobian, and a drag asks the rank before running whether the pull can move anything at all instead of grinding through a hundred refused iterations. We only report a global DOF number.
  • Adaptive damping with step acceptance. Both of us form the same damped normal equations (JᵀJ + λI) δ = -Jᵀr — the difference is what λ does. Ours is a fixed Tikhonov 1e-6 and every step is taken unconditionally; theirs is a real Levenberg-Marquardt trust region: λ scaled by the largest diagonal of JᵀJ (so it is relative to the sketch's scale, where our absolute constant is not), a trial assembly, the step kept only if the residual norm fell, and λ multiplied by 8 on rejection and by 0.3 on acceptance. Plus a stall test on the relative improvement, which stops a run against a system that has no solution — we only stop on max_err < 1e-6 or 100 iterations. That accept/reject loop is the robustness near degeneracies, not the presence of λ.
  • Linear algebra and Jacobians. They factor the lower triangle with a hand-written Cholesky and answer a non-positive pivot by damping harder; we run an nalgebra SVD of the normal matrix every iteration — rank-tolerant, but far more flops, paid back by our subsystem decomposition making each n smaller. Their partials are written by hand through a JacobianRow; ours come from forward-mode autodiff per (equation, variable), which is what makes a new constraint kind cheap to add for us and a piece of calculus for them. Their tolerance is 1e-10 on f64 throughout; ours is 1e-6 because the sketch boundary is f32.
  • allocation budget tests (dhat) and golden-image visual tests as first-class gates.

Where we are ahead of it:

  • primitives. They have points, segments and circles — no arc entity at all (arcs exist only as pieces of a circle inside arrangement), no conics. We have circular arcs in canonical start_angle + sweep_angle form plus arcs of ellipse / hyperbola / parabola with internal curve constraints.
  • constraint coverage. 14 variants against our 28 (three of ours are weak drag targets): they have no angle dimension, no symmetry, no midpoint, no diameter, no circle-circle tangency, no equal-radius across arcs, no axis constraints. They do have Spacing (parallel edge-to-edge dimension), which we do not.
  • tools and editing. Rectangle, trim, split, fillet, chamfer, snapping with priorities (endpoint / midpoint / center / quadrant / on-curve / axis) against their point, line, circle and dimension. Their equivalent of snap is auto-constraint on click (Anchor): a click on an edge or a rim builds a PointOnSegment / PointOnCircle instead of merely landing there.
  • solver decomposition. We partition the parameter graph into independent subsystems (mm_solver::partition, our enclave track); they solve one dense system for the whole sketch.
  • deterministic input-trace replay (DST) for interaction testing.

License

Licensed under either of

at your option.

vendor/egui and vendor/bevy_egui are patched copies of upstream crates and stay under their own upstream licenses. Bundled fonts in assets/fonts/ are third-party: DejaVu Sans (Bitstream Vera / DejaVu license) and Anonymous Pro (SIL Open Font License 1.1).

About

2D parametric sketch editor — Rust + Bevy + egui, with a Gauss-Newton geometric constraint solver

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages