BCad fork

Timeline
Login

Timeline

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

50 most recent check-ins

2026-07-18
09:22
Commented out redundant argument checks. leaf check-in: f9288d2c96 user: johnfound tags: openscad_lang
09:08
Fixed arguments handling for functions. check-in: 34219982ac user: johnfound tags: openscad_lang
07:43
Migrate 6 remaining module handlers to _leaf_scope and fix parse_kwargs mixed named+positional args.

Module handlers (_handle_text, _handle_line, _handle_polygon, _handle_square, _handle_circle, _handle_test_internal_occt) now use _leaf_scope instead of manual push_stack/parse_kwargs/pop_stack, matching move/arc/spline pattern.

Fix parse_kwargs: named args no longer advance the positional counter. Positional args now skip parameter slots already claimed by named args, matching OpenSCAD semantics for mixed calls like fn(c=1, 7, 8) -> a=7,b=8,c=1. check-in: 86ae6723a0 user: johnfound tags: openscad_lang

07:41
Remove --nosync from Fossil docs — not recommended, let Fossil handle sync. check-in: 250e1adad3 user: johnfound tags: openscad_lang
07:38
Update Fossil reference in AGENTS.md with comprehensive command docs.

Replaced minimal Git-like reference with detailed Fossil command tables covering status, file ops, committing, history, and remote operations. Added key Fossil concepts vs Git section and explicit warnings about non-existent Git commands (no fossil log, no staging area, etc). check-in: cc3ecb0bca user: johnfound tags: openscad_lang

07:34
Migrate 6 remaining module handlers to _leaf_scope and fix parse_kwargs mixed named+positional args.

Module handlers (_handle_text, _handle_line, _handle_polygon, _handle_square, _handle_circle, _handle_test_internal_occt) now use _leaf_scope instead of manual push_stack/parse_kwargs/pop_stack, matching move/arc/spline pattern.

Fix parse_kwargs: named args no longer advance the positional counter. Positional args now skip parameter slots already claimed by named args, matching OpenSCAD semantics for mixed calls like fn(c=1, 7, 8) -> a=7,b=8,c=1.

Also make pythonocc TKCAF sed patch idempotent in cross-build-wine.sh. check-in: fb80c0dbc4 user: johnfound tags: openscad_lang

06:10
Add cross-platform font resolver, bundled LiberationSans font, and bcad_hull build script. text() now works on Windows via FontResolver (Linux: fc-list, Windows: winreg, macOS: directory scan). Removes scipy/numpy font lookup and platform-specific manual search. check-in: d50dd4c9fe user: johnfound tags: openscad_lang
2026-07-17
20:05
Working hull() in windows. Some problems with the functions arguments order. check-in: 38af001599 user: johnfound tags: openscad_lang
19:14
Add bcad_hull C++ library, remove old packaging dirs, update cross-build

- cpp/bcad_hull/: C++ convex hull library (QuickHull, SWIG bindings) replaces scipy dependency for hull() operation - Remove packaging/{freetype,harfbuzz,imgui-bundle,occt,pythonocc-core} (old vendored build scripts, no longer needed) - cross-build-wine.sh: add bcad_hull cross-compilation, add --add-data for Python files (__init__.py, _swig.py), fix imgui nanobind hack - imgui_color_text_edit.patch: regenerated - scl_context.py: hull() uses bcad_hull instead of scipy - main_window.py: remove Development submenu, unused render code check-in: df0b00253d user: johnfound tags: openscad_lang

14:33
WIP: cross-build fixes (untested)

- toolchain-mingw64.cmake: INCLUDE/PACKAGE changed from BOTH to ONLY - cross-build-wine.sh: Visualization=ON, WRAP_VISU=ON, various fixes - text_shaper CMakeLists.txt: WIN32 Python library linking check-in: 729cd8bcf4 user: johnfound tags: openscad_lang

14:32
Add distribution size analysis (604 MB breakdown, optimization opportunities) check-in: 1827e9bd5b user: johnfound tags: openscad_lang
05:52
Fixes to the cross-build-wine.sh, but still not working. check-in: 1e864b4ec7 user: johnfound tags: openscad_lang
2026-07-16
14:45
Minor change in cross-build-wine.sh check-in: 105a35d5b7 user: johnfound tags: openscad_lang
14:22
Fixes to the script. Added symlinks to the source directories for cross compilation. check-in: fa74e282b2 user: johnfound tags: openscad_lang
14:11
Windows cross-build: from-source pipeline for OCCT/HarfBuzz/FreeType/pythonocc

- Rewrite cross-build-wine.sh: replace conda installs with MinGW cross-compilation from source (FreeType, HarfBuzz, OCCT, pythonocc-core) - Add bcad_text_shaper cross-compilation step using MinGW-built deps - Update CMakeLists.txt: manual HARFBUZZ/FREETYPE path specification - Not fully tested yet check-in: 60514ae414 user: johnfound tags: openscad_lang

10:54
Refactor progress system: replace string labels with integer function IDs

- Replace progress_label (char * 256) with progress_func_id (int32) in shared memory - Rewrite progress_helper.py: FUNC_* constants, func_name() mapping, cancel-only logic - Remove all label stack operations (push_label/pop_label/set_progress_label) - Remove init_progress/advance_progress/adjust_progress_total functions - Use direct state.s writes + old_id save/restore pattern in scl.py and scl_context.py - Remove _update_progress_state() with string encode/padding overhead - Update bcode_tools.py GUI to read func_id and convert via func_name() - Benchmark2 (1M empty loop iterations): 8506ms → 5818ms (-32%) check-in: 0d6f131167 user: johnfound tags: openscad_lang

09:07
Add builtin function argument validation and handler hardening

- Add _validate_func_args() to check arg count + type before calling handler - Add _type_name() and _TYPE_CHECKERS for validation - Dispatch returns undef + WARNING on count/type mismatch (OpenSCAD-compatible) - Fix len: return undef for non-list/string instead of 1 - Fix is_num: bool subclass is not a number - Fix chr: range check 0..0x10FFFF - Fix cross: support 2D vectors (scalar result), validate 3-element for 3D - Fix ln/log: domain check (positive numbers only) - Fix norm: validate all elements are numeric - Fix min/max: validate all elements are numeric, handle empty lists - Add min_count for rands (3) and assert (1) - Add 22 test files in tests/builtin_args/ (one per builtin function) - Add testing rules to AGENTS.md check-in: 2cf6a65426 user: johnfound tags: openscad_lang

07:57
Add capture() module, geometry inspection functions, and testing library

- Add SCLCapture class for collecting child shapes into compound - Add capture('name') { ... } module to store geometry in parent scope - Add 8 inspection functions: bbox_min, bbox_max, volume, surface_area, centroid, num_faces, num_vertices, num_edges - Add tests/lib/testing.scad with check_approx() function using norm() - Refactor 7 test files to use capture+assert(check_approx(...)) with analytical expected values instead of echo-only verification - Fix rotate() with no args: call ctx.rotate() to produce identity transform so capture() can store the resulting shape - test_capture.scad, test_rotate_openscad.scad, test_multmatrix.scad, test_linear_extrude_center.scad, test_resize.scad, test_fn_threshold.scad, test_import_center.scad check-in: b2b2730f61 user: johnfound tags: openscad_lang

06:35
Fix rotate() argument handling for OpenSCAD compatibility

- When a is a vector (Euler angles), v is now properly ignored with warning - When v is passed without a, emit warning (rotation on undef is meaningless) - Rewrite _handle_rotate: three clear branches (vector a, scalar a, no a) - Add tests/test_rotate_openscad.scad: 13 test cases verified against OpenSCAD check-in: 8a1ea30ab8 user: johnfound tags: openscad_lang

05:43
Fix rotate() scope; centralize scope isolation via _module_scope/_leaf_scope

- Fix rotate_module_definition: add 'a' parameter alongside 'v' (was only 'v'), remove dual-path arg handling in _handle_rotate. rotate(45) = Z-rotation, rotate(90,[1,0,0]) = axis rotation, rotate([0,0,45]) = Euler angles.

- Add _module_scope context manager: block parsed in clean sandbox frame, sandbox popped BEFORE kwargs parsed. Prevents module args leaking to children.

- Add _leaf_scope context manager for leaf modules (no block, no context).

- Refactor 21 block+kwargs handlers to _module_scope: translate, rotate, mirror, scale, multmatrix, resize, color, loft, union, intersection, difference, hull, minkowski, offset, group, render, projection, linear_extrude, rotate_extrude.

- Fix loft scope leak: sandbox frame was staying on stack during kwargs eval.

- Refactor 10 leaf handlers to _leaf_scope: move, arc, spline, close, fillet, polyhedron, cube, cylinder, sphere, import.

- Add scl_examples/test_args_edge.scad: 11 rotate() edge-case tests. check-in: 937777c08a user: johnfound tags: openscad_lang

04:33
Remove debug_parser/debug_expr dead code; fix color() scope isolation (v->c)

- Remove debug_parser=False, debug_expr_en=False, debug_expr() and all 38 if-debug_parser guards (block handlers, profile primitives, simple shapes, import, eval_scope). Net -121 lines in scl.py. - Rename color_module_definition param v -> c (OpenSCAD API). - Restructure _handle_color: parse_block before parse_kwargs in separate stack frames to prevent module args leaking into children. - Update AGENTS.md and docs/scl.md accordingly. check-in: edaf51fe12 user: johnfound tags: openscad_lang

2026-07-15
14:47
Partially working text() and textmetrics(); Strange problem with the created object alignment. check-in: 09a36a69c4 user: johnfound tags: openscad_lang
12:13
Fix textmetrics: use pen advance for advance, add bbox to TextShapeResult

- Add xmin/ymin/xmax/ymax (ink bbox coordinates) to TextShapeResult so Python can use them directly without recomputing via brepbndlib - _get_text_metrics(): call shape_text() directly instead of going through _make_text_shape(), use pen advance for advance (was incorrectly using bbox width, which is always <= pen advance) - _make_text_shape(): fix valign formulas — use ymax/ymin from bbox instead of h (bbox height). valign=bottom was identical to valign=top (copy-paste bug: both used dy=-h) - Update AGENTS.md: document new bbox methods on TextShapeResult check-in: dedfb4e315 user: johnfound tags: openscad_lang

11:50
Convert bcad_text_shaper from nanobind to SWIG and clean up API

- Replace nanobind with SWIG 4.4.1 (same version as pythonocc) for Python bindings - TextShapeResult: convert from plain struct to proper C++ class with private data (m_shape, m_width, m_height) and public const methods (shape(), width(), height(), is_null()). shape_text() is a friend function. - Remove BRep serialization hack (C++ no longer writes BRepTools::Write string) - TopoDS_Shape typemap: use SWIG_TypeQuery() for runtime type lookup instead of compile-time SWIGTYPE_p_TopoDS_Shape — avoids overwriting pythonocc's clientdata in shared SWIG runtime (fixes ShapeType AttributeError) - __init__.py: thin re-export (import OCC.Core.TopoDS + re-export from _swig), no manual wrapper classes - Remove nanobind dependency from pyproject.toml - Simplify build.sh: use pip install instead of cmake+manual copy - Delete rebuild.sh, _bcad_text_shaper.map (no longer needed) - Fix OutlineContext memory leak: destructor closes current wire, cb_move_to allocates new wire (was previously leaking on every move_to) - Update scl.py consumer: result.shape -> result.shape(), result.width -> result.width() - Update AGENTS.md: document SWIG binding pattern, TextShapeResult class API check-in: b55c3b529a user: johnfound tags: openscad_lang

05:52
Add bcad_text_shaper: C++ HarfBuzz+FreeType+OCCT text shaping for OpenSCAD-compatible text()

- New C++ extension (cpp/bcad_text_shaper/) using HarfBuzz for shaping, FreeType for outlines, OCCT BRepBuilderAPI_MakeFace for faces - Proper RTL (Arabic, Hebrew), CJK, ligatures, kerning, vertical text - Auto-detect direction/script from text content via HarfBuzz APIs - Face orientation fix: ShapeFix_Face + BRepAdaptor_Surface normal check - Space handling: pen advance before glyph skip so spaces count in width - BRep serialization: C++ writes BRepTools::Write, Python deserializes - em parameter: matches OpenSCAD effective_size = em * 72.0 / 100.0 - direction/language/script/em params added to text() module definition - direction default changed from 'ltr' to '' for auto-detect (OpenSCAD compat) - spacing parameter now actually passed through (was silently ignored before) - 37 tests passing (12 basic + 25 exotic scripts) - AGENTS.md documented with full API and implementation details check-in: f414607b31 user: johnfound tags: openscad_lang

2026-07-14
14:44
docs: mark text() as implemented in compatibility todo check-in: 89404ee09b user: johnfound tags: openscad_lang
14:42
text, textmetrics, fontmetrics: add OpenSCAD text functions

- text() module: renders text as 2D geometry via OCCT StdPrs_BRepFont font resolution via fc-match, default Liberation Sans supports size, font, halign, valign, spacing parameters creates proper child SCLPart3 context (fixes linear_extrude integration) - textmetrics() builtin: returns position, size, ascent, descent, offset, advance - fontmetrics() builtin: returns nominal/max ascent/descent, interline, font info - expr_dot extended for dict access (tm.size[0], fm.nominal.ascent) - 12 test cases all PASS check-in: d48ee6fbf1 user: johnfound tags: openscad_lang

13:54
intersection_for: add OpenSCAD doc examples to tests

Test cases from OpenSCAD documentation: - Rotating spheres with scalar range - Rotating cubes with vector-of-vectors list - Empty range (no-op) Total: 7 tests, all PASS check-in: 954de721df user: johnfound tags: openscad_lang

13:51
intersection_for statement

New statement: intersection_for(i = range) { block } - Executes for-loop, intersects all iteration results - Same syntax as for: single/multiple counters, nested - Uses SCLIntersection context + intersection() method - Empty range produces no result (no-op) - Cache key: 'intersection_for'

Lexer: INTERSECTION_FOR token (reserved word) Parser: intersection_for_loop rule, stat_intersection_for AST node Evaluator: stat_intersection_for handler in eval_scope Tests: test_intersection_for.scad (5 tests) check-in: a0e43d9bbd user: johnfound tags: openscad_lang

13:39
multmatrix and resize transform modules

multmatrix(m): - Arbitrary 4x4 affine matrix transform via gp_GTrsf - Top 3x3 vectorial part (rotation/scale/shear) + translation column - SCLMultmatrix class in scl_context.py

resize(newsize, auto): - Scale to absolute dimensions via bounding box measurement - newsize[i] > 0: scale to target, 0: leave unchanged (or auto-scale) - auto: single bool (all axes) or [bool,bool,bool] per-axis - Scalar newsize ignored (OpenSCAD compat: stays [0,0,0]) - auto as number ignored (must be bool, matching OpenSCAD) - SCLResize class in scl_context.py

Tests: test_multmatrix.scad (6), test_resize.scad (10) Docs: openscad_compatibility_todo.md check-in: dd34c6aedb user: johnfound tags: openscad_lang

12:28
each keyword and multi-generator list comprehensions

- Add 'each' keyword to lexer (reserved word) - Parser: 'each' only valid inside [...] via vector_element rule, NOT as standalone expression (matches OpenSCAD behavior) - Parser: multi-generator comprehensions [for(a=...) a, for(b=...) b] parsed as expr_array_multi_for with sequential (not Cartesian) semantics - Parser: 'each' as array_for_body for use inside for comprehension bodies - Evaluator: expr_each handler with _each_flatten (list passthrough, string→characters, scalar→single-element list) - Evaluator: expr_array_multi_for iterates generators sequentially, concatenating results - array_list evaluator extends (not appends) for expr_array_for and expr_each elements - is_nested detection extended to include expr_each for flatten-in-for - Tests: test_each.scad with each, multi-generator, mixed, regression - Docs: openscad_compatibility_todo.md, AGENTS.md check-in: fd8bcef671 user: johnfound tags: openscad_lang

11:40
rotate_extrude: polygonal revolution path for $fn <= threshold

When $fn <= threshold, rotate_extrude creates a polygonal revolution path (N flat segments) instead of a smooth circular sweep, matching OpenSCAD behavior.

- Read $fn in _handle_rotate_extrude, pass to context, include in cache key - SCLRevol.rotate_extrude(): two paths — smooth (BRepPrimAPI_MakeRevol) and polygonal (ThruSections with ruled=True) - OpenSCAD segment formula: ceil(max($fn, 3)) * abs(angle) / 360 - CheckCompatibility(False) — wires are rigid rotations, already compatible - Partial angles work correctly (e.g. angle=180 with $fn=6 → 3 sections) - Add tests: partial angle rotate_extrude (180°, 90°) - Update docs/openscad_compatibility_todo.md and AGENTS.md check-in: d568e0f993 user: johnfound tags: openscad_lang

10:46
polygon threshold: configurable preference for geometry vs quality intent

- Add fn_polygon_threshold setting (default 12) in Preferences > General - <= threshold creates polygon (hexagonal prism, etc.) - > threshold creates ideal geometry (smooth circle/cylinder) - == 0 always creates ideal geometry - Threshold check in cylinder() and circle() context methods - BcadState shared memory field for cross-process access - Settings: DEFAULTS, push_state(), preferences_dialog UI - Add tests: test_fn_threshold.scad (12 tests) - Update docs/openscad_compatibility_todo.md and AGENTS.md check-in: 0d76263363 user: johnfound tags: openscad_lang

08:47
OpenSCAD compatibility: // params, linear_extrude center/slices, import center, circle polygon

- linear_extrude: add center=true/false (Z-centering via bounding box) and slices=N (accepted, ignored) - import: add center=true/false (centers all imported shapes as group via shared bounding box) - circle: accept parameter — generates N-sided polygon approximation (like cylinder does for prisms) - ///: suppress 'not specified as parameter' warning for all hBcprefixed variables passed to any module - Remove from cylinder_module_definition (unnecessary — set_variable works regardless of arg_list) - Move 14 local OCC imports to top-level in scl_context.py (cleaner code, same performance) - Add center_z() to SCLExtrude, _center_children() helper for import centering - Add tests: test_linear_extrude_center, test_linear_extrude_slices, test_import_center, test_special_vars_no_warn - Add docs/openscad_compatibility_todo.md tracking 17 missing features check-in: 38da8a2486 user: johnfound tags: openscad_lang

2026-07-13
20:16
Fix measure mode toggle: send RQ_REFRESH to switch OCCT selection mode

Measure mode was toggled in shared memory (state.s.measure_mode) but never sent to worker via IPC. Add rq_refresh() call after toggle so RQ_REFRESH handler reads the new value and calls call_set_measure_mode() which switches OCCT between vertex and shape selection modes. check-in: 7fcdd64786 user: johnfound tags: openscad_lang

19:58
Replace 3D AIS_Point center marker with 2D XOR crosshair

Remove AIS_Point-based center-of-rotation marker from boccviewer.py (_center_marker, update_center_marker, all 9 call sites in offscreen_display.py, Repaint hook).

Draw 2D crosshair directly on pixel buffer in update_img() using XOR inversion on BGR channels (visible on any background). Stroke length 13px, single-pixel width.

Add show_center_crosshair preference (display section, default True) with checkbox in Preferences > Display. Stored in BcadState shared memory, applied on startup and preference apply. check-in: 6b757f9127 user: johnfound tags: openscad_lang

19:28
Move rotation center logic from scroll to double-click, simplify navigation

Double-click left button: center camera on picked point (surface or OBB based on preference, Ctrl inverts) + cursor moves to viewport center. Uses RP_CENTER_DONE reply for cursor repositioning.

Scroll: pure zoom only, no pick-point centering, no _center_shape state.

Pan: remove adjust_distance_to_scene() auto-distance adjustment.

Remove from boccviewer.py: distance_to_nearest_along_axis(), adjust_distance_to_scene() (no longer used).

Remove _center_shape tracking from offscreen display.

New IPC: RQ_DOUBLE_CLICK (rq_double_click with x, y, ctrl). Double-click detection moved to is_item_active() block (before rotation logic) so it fires correctly when mouse button is pressed. check-in: 9a34db56b7 user: johnfound tags: openscad_lang

19:11
Fix axis display at startup and replace deprecated brepbndlib_AddOBB

- Add _update_axis_length() call in call_set_size() so coordinate axes are drawn on the first frame (RQ_SET_SIZE arrives immediately at startup). Previously axes only appeared after a camera action or code reload. - Replace deprecated brepbndlib_AddOBB() function with static brepbndlib.AddOBB() method (deprecated since pythonocc-core 7.7.1). - Fix BRepIntCurveSurface_Inter: use W() instead of non-existent Parameter() method for closest-intersection selection. check-in: 03f57edd58 user: johnfound tags: openscad_lang

18:49
Camera navigation: zoom-to-cursor, auto distance, scroll center mode preference

Zoom-to-cursor: scroll wheel zooms toward cursor position using pixel-offset compensation (Pan correction after ZoomFactor/SetScale). Cursor position passed through rq_scroll/rq_zoom IPC chain (rqq -> worker_engine -> offscreen_display).

Two-phase scroll: first scroll on object re-centers camera (Eye stays, Center moves to picked point along eye-to-point direction, no teleport). Second scroll on same object = zoom only. _center_shape tracks which object was centered; reset on pan.

Auto distance on pan: after Pan(), adjust_distance_to_scene() finds nearest AIS_ColoredShape along camera axis via Context.MoveTo(gp_Ax1), then sets Center distance to match. Keeps camera distance proportional to scene depth.

Scroll center mode preference: Surface point (picked intersection) or OBB center, configurable in Preferences > Display. Ctrl+scroll inverts the mode. Stored in BcadState.center_mode (shared memory) and settings.conf.

Pick point fix: pick_point() and distance_to_nearest_along_axis() now iterate all BRepIntCurveSurface_Inter intersections and select closest by W() parameter instead of returning the first (topology-order) hit.

Camera Up fix: after SetEyeAndCenter in scroll re-center, compute proper Up by projecting world-Z onto plane perpendicular to camera direction (avoids degenerate Up when looking along Z axis).

Restored oriented_bbox_center() on OffscreenRenderer for OBB mode. check-in: 74e175dc1a user: johnfound tags: openscad_lang

14:08
Zoom-to-cursor: use picked point instead of OBB center, remove center_shape tracking

Replace OBB center with picked point (ray-surface intersection) as camera center target. For long objects, camera now centers on where the user actually clicked rather than the geometric center of the entire shape.

Remove _center_shape tracking: every scroll on an object immediately re-centers and zooms, no two-phase behavior. Cursor still moves to viewport center via RP_SCROLL_CENTER after re-centering.

Remove oriented_bbox_center() from OffscreenRenderer (now unused). Remove debug print from axis(). Remove dead _axis_length/_axis_center fields from SCLDisplay. check-in: 218b1adbcb user: johnfound tags: openscad_lang

13:37
Fix axis lines to follow camera center and extend beyond viewport

Axis lines now pass through [0,0,0] but extend far enough to reach the camera center and cover the viewport area.

Formula: half_length = |center_projection_on_axis| + 2 * viewport_diagonal This ensures axes always pass through origin and reach the camera center with sufficient margin.

Fix MaximalParameterValue: OCCT StdPrs_Curve::FindLimits doubles delta until 2*delta >= MaximalParameterValue, so MPV must be set to 2x the desired half-length to get the correct visual extent.

Axis update moved from update_img() (every frame) to specific handlers: call_scroll, call_pan, call_zoom, call_set_projection, call_view_action, call_set_view - only when camera actually moves or zoom changes.

_update_axis_length() simplified: no threshold, just Remove+recreate. check-in: a7b9649622 user: johnfound tags: openscad_lang

11:55
Viewport: center marker, pick-based zoom-to-cursor, and orbit center visualization

- Add red AIS_Point center marker on OffscreenRenderer that tracks camera.Center() position and updates on every Repaint() - Implement pick_point() using DetectedInteractive() + AIS_ColoredShape DownCast (type-check via DynamicType().Name() to avoid RuntimeError) with BRepIntCurveSurface_Inter ray-shape intersection - Add oriented_bbox_center() using BRepBndLib.brepbndlib_AddOBB for Oriented Bounding Box computation (gp_XYZ -> gp_Pnt conversion) - Scroll zoom: pick object under cursor -> pan camera (SetEyeAndCenter) so object's OBB center becomes new Center; if same object or no hit, do normal zoom (ZoomFactor). Send RP_SCROLL_CENTER reply. - GUI handles RP_SCROLL_CENTER by moving cursor to viewport center via glfw.set_cursor_pos for consistent subsequent scroll behavior - Pan resets _center_shape to allow re-picking the same object - rq_scroll now carries cursor coordinates (delta, x, y) check-in: 80c3fdd99e user: johnfound tags: openscad_lang

2026-07-12
19:17
Fix trihedron drift on camera tilt (turntable rotation)

Orthogonalize camera Up vector after SetEye()+SetUp() in _turntable_rotate() using Gram-Schmidt projection.

OCCT bug: ComputeApply() for Graphic3d_TMF_TriedronPers uses raw camera.Up() for Y offset computation, but OrientationMatrix() internally orthogonalizes Up via Cross(Side, Forward). When Direction and Up are not orthogonal (phi != 0 in turntable mode), the offset is scaled by cos(phi), causing the trihedron to drift toward viewport center on tilt. check-in: b7127fd683 user: johnfound tags: openscad_lang

17:16
Add corner bracket highlighting (matchingBracketCorners) - Replace rectangle-based bracket background highlight with 4 corner brackets (L-shaped) per bracket - Add matchingBracketCorners to Color enum (dark: RGB 220,60,60; light: RGB 200,40,40) - Each corner: 2 lines (horizontal + vertical), thickness 1px, size 25% of glyph cell - Baseline offset +3px for curly braces, +1px downward extension on bottom-right corner for crisp pixel - Update imgui_color_text_edit.patch check-in: 7a3abf7518 user: johnfound tags: openscad_lang
16:38
Fix apply_quality() using RTTI type check + breptools.Clean + Redisplay

- Fix apply_quality(): use DynamicType().Name() RTTI check before DownCast to avoid SWIG pending exceptions; use breptools.Clean() to reset cached Poly_Triangulation; use Context.Redisplay() instead of Erase+Display cycle - Fix doubled axis lines: SCLDisplay.reset() called axis() twice, creating duplicate AIS_Line objects; remove redundant call and dead commented code - Remove redundant set_bg_gradient_color() from reset() (View property, not affected by RemoveAll) - Remove unused offscreen_display.call_set_quality() check-in: 93431c83f7 user: johnfound tags: openscad_lang

10:22
Move root_first to user preference with default True (OpenSCAD compat)

- Add root_first field to BcadState shared memory - Add 'general.root_first' setting with default True - Add checkbox in Preferences > General tab with description - Remove self.root_first flag from SCL class, read from state.s.root_first - True = outermost ! wins (OpenSCAD), False = innermost wins (debug) check-in: 1903c6814c user: johnfound tags: openscad_lang

10:02
Autocomplete: smart replacement and cursor positioning

- Fix #1: replace only search term, not entire word after cursor (use searchTermEnd instead of findWordEnd which ate following text) - Fix #2: skip adding () when '(' already follows the word (handles optional whitespace between name and paren) - Fix #3: no-arg modules (union, difference, hull, etc.) get cursor after ')' instead of between '()' via new 'M' type tag check-in: 4dd11a9183 user: johnfound tags: openscad_lang

2026-07-11
10:50
Quality preferences. Now need F5 after apply. check-in: 35c542f968 user: johnfound tags: openscad_lang
08:46
Remove Singleton class — all globals now live in bcad_state module

Move settings, scl, sd, pipe, img from Singleton to module-level globals in bcad_state.py. All 10 consumer files updated to use 'state.xxx' via 'from bcad.binterpreter import bcad_state as state'. Delete singleton.py. check-in: 9f372d6c77 user: johnfound tags: openscad_lang

08:30
Move state out of Singleton into bcad_state module as global s/s_shm

- Add module-level globals s (BcadState) and s_shm (SharedMemory) to bcad_state.py - Replace all Singleton.state access with bcad_state.s (via 'import bcad_state as state') - Replace all Singleton.state_shm with bcad_state.s_shm - Move progress helper private vars (_progress_total, _progress_current, etc.) from Singleton attributes to progress_helper.py module-level globals - Pattern: 'from bcad.binterpreter import bcad_state as state' then state.s.field - Singleton remains for settings, scl, sd, pipe, img (to be removed later) check-in: 624e3aff25 user: johnfound tags: openscad_lang