Add Amharic language support - #346
Conversation
Remove push_transform_to_model from WorkPieceElement and GroupElement, remove no-op trigger_ops_rerender stub and call site, remove circumferential_axis property from CanvasProjection and its test.
Remove unused SceneRenderer Protocol, unused mvp_ui_gl/mvp_scene_gl properties from RenderContext, drop Protocol import.
Remove close_handle and forget from ArtifactStore, remove encoded_output_bytes from JobArtifact (param, field, lazy-decode, serialization). Update 3 test files.
Delete CairoEncoder, TextureEncoder and their test files. Remove stitch_chunk_to_bitmap function and its tests.
Remove WorldSpace, WorkareaSpace, and PixelSpace classes from coordspace and their tests.
fix(mac): restore Monterey-compatible bundles
Normalize OpenCV ChArUco corners and IDs before conversion so both singleton-dimension and flat result shapes are supported. Reject malformed or incomplete results and add regression coverage for the supported shapes and invalid detections.
…apes Fix ChArUco detection array shapes
The settle-driven preview refresh re-triggered job generation forever when the document could not produce a job aggregate. The intent builder also emitted job nodes referencing steps with no compute upstream, causing a missing-dependency error on every rebuild. Skip job/machinexform/encode nodes when no step has upstream compute, and fail fast in generate_job_artifact when the doc has no visible step with workpieces.
…nings - Workpiece.to_part() now uses Part.from_geometry_multi_face so disjoint pockets become separate faces instead of a single merged contour - Add assembly_warnings module translating raygeo AssemblyWarnings into user-facing strings - Emit pipeline_warnings from the intent controller on completed nodes with warnings, relayed through the pipeline and shown as toasts - Bump raygeo to 1.28.0
Add a new built-in addon 'CNC Essentials' providing mill capabilities:
- MILL capability with tool diameter, spindle RPM, feed/plunge/travel
speeds, target depth, depth-per-pass and safe Z settings.
- Step base (CncAssemblerStep) that builds raygeo assembler specs and
compute payloads, plus steps for adaptive clearing, flat spiral,
helix plunge, ramp entry, slot, toroidal clear, profile inner and
profile outer.
- Addon worker/frontend entry points and a CNC settings widget.
Multi-face and sequential CNC support:
- WorkPiece.to_part() now uses Part.from_geometry_multi_face so
disjoint pockets become separate faces.
- Steps can opt into sequential global state (uses_global_state):
intent builder chains Compute steps via state_source_keys, links
aggregates sequentially (LinkMode.sequential) and feeds the
predecessor token into position-sensitive caching.
- Pipeline surfaces non-fatal assembler warnings (FACE_FAILED /
REGION_FAILED) through a new assembly_warnings signal; the doc
editor translates them (gettext) into UI toasts.
Bump raygeo to 1.28.0 in pixi.toml, requirements.txt and
debian/requirements-bundle.txt, and update pixi.lock.
Tests: capability MILL registration, multi-face workpiece to_part,
assembly warning translation, sequential-state intent controller
behaviour, and adaptive clearing step spec generation.
Introduce MachineCapability (LASER/MILL) computed as explicit config union head-inferred capabilities, and generalize heads into a Head base with LaserHead and SpindleHead subtypes. Steps declare required machine caps and the step pickers filter by the active machine, so CNC steps only appear once a spindle is configured. Machine settings gain a Heads page (add/remove Laser/Spindle heads with type-specific detail widgets) and a Capabilities readout page. ConfigWizard asks for head type on review.
CncAssemblerStep and its concrete subclasses did not override to_dict/from_dict, so CNC parameters (tool_diameter, spindle_rpm, target_depth, etc.) were silently dropped on save. Add polymorphic serialization overrides plus a Step._serialized_keys() hook so unknown keys from newer files still land in extra instead of being mistaken for known CNC attributes.
Rename the step head-selection attribute and serialization key to be domain-neutral. get_selected_laser() becomes get_selected_head() and returns a generic Head. Old project and recipe files load via alias migration, keeping the legacy key out of extra. Also restrict LaserHeadVar choices to laser heads (via the machine_capability property, avoiding a circular import) and guard the kerf-sync handler so selecting a SpindleHead cannot crash.
Create LaserStep(Step) owning the laser process attributes and behaviour (initial ops, summary, typed get_selected_laser, laser setters, serialization overrides). The base declares the attributes before super().__init__ so capability defaults still apply. All six laser step classes now inherit from it. Purely additive - core Step keeps its laser surface until the later removal iteration.
EngraveStep overloaded max_power (laser hardware max in mW) as its raster power ceiling (0-1 float), colliding semantics under one serialized key. Rename the raster range to min_power_level / max_power_level. LaserStep.max_power is now cleanly typed as int. Legacy engrave files keyed the raster range as min_power/max_power; from_dict migrates those into the _level attributes and resets the hardware max_power slot instead of inheriting the raster ceiling. EngraveStep gains _serialized_keys for its raster keys.
Pass the Machine to build_compute_payload/assembler_token_params and let each domain step resolve its own process parameters from it. This eliminates the laser-flavoured MachineDefaults object that was shared through the pipeline and removes it from core entirely. Laser steps read the machine (arc tolerance, head spot) directly via LaserStep.get_laser_spot; CNC steps ignore it. The pipeline and its controller now require a machine, so the step methods' machine parameter is typed non-optional. CNC payload power is expressed as the spindle RPM / max RPM ratio so toolpaths render at the right intensity instead of the zero-power colour.
Move laser-only attributes (power, kerf, air assist, etc.) from the core Step into the laser addon's LaserStep base and its concrete steps. Steps now own their import-settings application via apply_import_settings, so the lightburn importer no longer special-cases laser attributes and the assembler no longer mutates step settings directly.
Build the op player before extracting playback offsets during the chunked artifact upload, so the ring/scanline renderer gets a valid playhead on the first render instead of drawing every vertex. Also upload the colour LUTs before vertex data and give the scanline overlay a power brightness ramp so it dims by power.
Drivers now report plain DriverFeatures data (PWMParams for PWM support) instead of building concrete Capability objects. The machine resolves a head's effective capabilities through an addon-registered resolver registry, so core no longer depends on the laser addon's PWMCapability. Move PWMCapability into the laser addon and resolve it there. Head spot resolution moves from LaserStep to a static LaserHead.get_spot_size, and head-specific process defaults move to Head.get_defaults. Steps no longer carry driver-capability or head-resolution methods.
The machine-transform stage now subtracts WCS offsets in machine space (after w2m) instead of world space (before w2m), fixing wrong G-code with axis reversal + WCS. Adds parametrized regression test covering all origin/reversal/WCS combinations.
Move display-facing coordinate conversions from MachineSpace to MachinePanel, which holds a reference to the Machine and derives a MachineSpace on demand. All UI callers (canvas, doceditor, simulator) now use machine.panel.* instead of machine.get_coordinate_space().*. MachinePanel gains proxy methods for world↔machine point/item transforms, workarea geometry, axis labels, and command offsets. A reference_position_world property replaces the removed Machine.get_reference_position_world method. Backend callers (encoder, frame generation) continue using MachineSpace.from_machine directly.
MachineSpace now operates purely in MACHINE_SPACE, WORLD_SPACE, and COMMAND_SPACE. All bed-rotation logic (PanelOrientation, rotation matrices, composed transforms, presented geometry) lives on MachinePanel. The panel observes machine.changed to reproject camera calibration when bed dimensions change. MachinePanel defines PanelOrientation (NATIVE / ROTATED_LEFT / ROTATED_RIGHT), owns the orientation state, composes the panel-to- native rotation on top of the native MachineSpace transforms, and exposes presented extents, margins, workarea size, and axis-label origin. Machine delegates panel_orientation and set_panel_orientation to the panel and serializes the orientation in to_dict/from_dict.
…ing for clamping events
…rker location tests
Add extent_frame and has_custom_work_area to MachinePanel so the presented geometry API is complete. ViewportConfig.from_machine_with_wcs now derives width/depth, model-matrix flips, margin_shift, extent frame, and axis flags from machine.panel instead of raw machine attributes. WorkSurface._machine_coords_to_canvas delegates to panel.machine_point_to_world() and _update_extent_frame_flat reads panel.extents/margins/workarea_size.
MultiSelectionGroup bounding boxes now use CanvasElement.get_world_bounding_box() instead of manual corner math, and the framing-selection path in Canvas consumes the same helper. Add MachinePanel.work_area_center() and replace three duplicate work-area center computations in array_dialog, layer_column, and drag_drop_cmd. Move selection bounds reporting from MainWindow into WorkSurface.get_selection_bounds(), which uses panel.get_workarea_world_rect() for the machine fallback.
Add MachinePanel.calculate_jog() which maps visual jog directions (EAST/WEST/NORTH/SOUTH) to native axis deltas through the composed world-to-machine matrix, so a rotated bed drives the orthogonal native axis. UP/DOWN delegate to Machine.calculate_jog for Axis.Z. JogWidget now aggregates per-direction deltas and checks joggability and soft limits against the axes a direction actually drives.
Add MachinePanel.machine_to_panel, the 90-degree bed rotation from MACHINE-bed geometry into the panel presentation (inverse of the panel-to-native rotation). Add MachinePanel.nogo_zones, which returns detached rotation-projected copies of the machine's no-go zones so callers never mutate the native zones.
Step renaming was not reflected in the WorkflowView (the step list in the main window's right pane) because: 1. Step.set_name() did not explicitly fire the 'updated' signal like Layer.set_name() does. While the inherited name property setter does fire it, adding the explicit send makes the notification path consistent with how Layer renaming works and ensures all listeners are reliably notified. 2. WorkflowView only listened to workflow.updated, workflow.descendant_added, and workflow.descendant_removed, but not workflow.descendant_updated. When a step's name changes, the signal bubbles up as descendant_updated, which WorkflowView was missing, so it never refreshed its step list. Added the missing connection. Closes barebaric#343
When importing with the Map to Existing layer mode, document layers that still carry auto-generated names (e.g. "Layer 1") are renamed to the imported layer's name, since they were never manually renamed. Manually renamed layers are left untouched. The rename is undoable as part of the import transaction.
|
Hi there, thank you for opening the PR. I see that the file has no actual translations though, I think those would be needed before merging this. I am also wondering about maintenance - without frequent updates I expect this will be outdated quickly. Are you willing to take on maintainership for this language? |
|
Hi @knipknap thanks for the feedback! I am actively working on the translation now and will be pushing updates to this PR as I progress. I am adding Amharic support primarily to help my employees operate our laser cutter for paper card production. Because of this specific focus, I plan to translate the core UI they’ll come across daily, but I likely won't be translating features outside that workflow. I completely understand if you'd prefer not to merge it without full coverage and ongoing maintainership, but I'm happy to provide these core strings if you're open to having a starting point for others to build on! What do you think? |
|
Ok, I guess it does not hurt even if this does turn out to be outdated. Thank you for the effort! |
|
@knipknap Thank you! I appreciate it! I plan to submit the translations in small batches as I work through them. Would it be okay to merge the initial batches along the way? That will help me test subsequent translations more easily. |
|
Perhaps you can use the attached AI translation as a basis? |
|
@knipknap Thanks for setting this up! Having this complete base file as a reference is going to save me a lot of time. AI often struggles with Amharic fluency, so there are bound to be inconsistencies and mistakes with technical terms, but it will be great to build on. I'll go through it, refine the core UI strings for our workflow, and push the updates. Out of curiosity, which tool did you use to generate the translations? |
|
Great, Thank you! I used aistudio.google.com with Gemini 3.1 Pro in this case - it does not allow uploading .po files, but I just copy/pasted the content in the chat box. It is too large for it to respond with everything in one response, but I just asked it to continue in a second prompt and then combined the two responses in one file. |
|
Ah, gotcha! Thanks for explaining the workflow. I'll dive in and push the updates to the PR soon. |
This PR adds the Amharic language code mapping to the general preferences UI page and initializes the base translation file directory structure under locale/am/LC_MESSAGES/.