Compare commits

..
Author SHA1 Message Date
SoftFever b67269ccc4 Fix z-fighting between feature previews and the bodies in the Design tab
The bodies now always draw over the preview on every face a feature leaves unchanged, for
all features. Since a hole's cut sits inside the body, the Hole preview now hides the
bodies and shows the result alone, as Fillet/Chamfer and Draft do.
2026-10-05 19:38:46 +08:00
SoftFever 6dfd1c2a0a Design tab: one rule for Enter/Esc/mouse, kernel fixes, Text and Revolve-axis features, studio lighting"th 7cp6pk upstream (#16019)
# Description

Follow-up to #15238. A review of the Design tab, and then testing its
Linux AppImage on desktops, turned up UX inconsistencies, kernel bugs
and a crash. This PR fixes them and fills the gaps found along the way.
Everything stays behind `SLIC3R_CAD` and the `enable_cad_feature`
preference. With the preference off, Prepare and Preview behave and
render exactly as before.

The first four commits are Design-tab fixes that landed on the fork
after #15238 and were never sent upstream: the value-field label, the
`GUI.hpp` include, sketch tool batches 9–14 and honest tool messages.
The later commits build on them.

**Interaction: one rule for mouse, Enter and Esc** (charter
`docs/CAD/cad_ux_guidelines.md` §4.2)
- Enter does what ✓ does; Esc does what ✗ does. Esc steps back one level
(value field → gesture → tool → selection) and never deletes anything.
- A click on empty space clears the selection and never applies a
pending operation.
- A right-click with nothing pending opens the offer. The click is
recognised by drift alone; the 200 ms timing rule is gone, since §6.2
forbids timing-dependent gestures.
- The panel's CHAR_HOOK owns Delete, Esc, Ctrl+Z/Y and F, so the
duplicate handlers in `GLCanvas3D` are removed.
- Ctrl+Z/Y work inside a sketch, and Edit ▸ Undo/Redo drive the Design
history while that tab is shown.
- A value the geometry cannot take (zero length, sweep > 360°, …) is
refused inside the field, and the field says why.

**Kernel**
- Solver: tangency and point-on-line pick the correct side;
circle–circle tangency is handled; the partitioned solve maps sentinel
references; constraints that could not be applied are reported instead
of silently dropped.
- Trim, extend, offset and mirror are fixed for arcs and ellipses.
Negative scale is handled, and zero-radius circles no longer produce
geometry.
- Feature → body references resolve by identity, so hiding, reordering
or deleting a feature no longer re-targets later features.
- Hole standards table corrected: inch countersinks are 82°. New threads
use the nominal diameter; new patterns use inclusive spacing. Both are
gated by flags, so existing recipes rebuild unchanged.
- **A closed loop that crosses or folds back is not a region.** Such a
loop passed the closed-loop check and MakeFace, and extruded into an
invalid solid with no caps. `SketchEngine::wires_to_face` now checks the
face (BRepCheck) and fails with a reason; `sketch_loop_defect()` finds
the crossing or cusp exactly, and the sketch tints the loop red and
marks the point.
- Recipe: saves origin, body colours and the loop auto-close setting in
a trailing block; the v4 reader is frozen as `load_flat_v4`; the 3MF
reader caps the entry at 1 GiB and the legacy entry name never overrides
the current one. New fields (`dressup_edges`, the Text parameters,
`revolve_axis_entity`) are appended at the end of the framed recipe, so
existing projects load and rebuild unchanged.
- OCCT failures are caught before `std::exception` in the new code (OCCT
≥ 8).

**Modelling**
- **Several solid edges in one Fillet/Chamfer.** Shift/Ctrl+click adds
or removes edges of the picked body; one feature dresses them all at one
size, every id resolved against the same body. The card and the offer
name the count.
- **Sketch Offset takes the whole outline** the picked curve belongs to,
with a live preview, instead of one segment (on a text outline a segment
is a fraction of a millimetre, so nothing seemed to happen).
- **Revolve about a line of the sketch.** The axis was only the sketch
plane's X or Y axis. It can now be any line of the profile sketch: a
construction centerline (preselected when the sketch has exactly one) or
an edge of the profile. The gizmo draws the axis dashed. A profile on
both sides of the axis is refused with the reason (MakeRevol failed
there with none). Surface Revolve takes the same axes.

**Panel, offer, text**
- The offer menu: its strings are translatable (the generator emits
`L()` markers, and the files are added to `list.txt`); it opens with a
title naming the selection; blocked verbs stay in place, greyed, with
their reason; each verb has one refusal wording.
- Extrude defaults to Join when the profile touches a solid, otherwise
New body. All result menus use one vocabulary: New body / Join / Cut /
Intersect.
- Interference and volume/area reports go to the status line, in
mm³/mm². Delete Body no longer asks for confirmation, since it is
undoable.
- **Text is its own feature** ("Text N" in the tree). Its dialog offers
any installed font (bold, italic) and the height in mm; it is modeless
and opens at the top right of the window, and the outline is drawn in
the view where it will go while typing. Editing the feature reopens the
dialog with its string, font and height. The outlines are saved too, so
the project opens the same on a machine without that font.
- New feature names match the card header (`Extrude 3`) and are
translated. Terminology and units are unified ("Coordinate system",
"Angle (°)").
- The Prepare Sketch/Primitive gizmos can be selected only when the CAD
feature is on.
- MCP: write methods are refused while the tab is busy (including while
the Text dialog is open); a timed-out command never runs; only a socket
is ever unlinked; `fillet`/`chamfer` accept an array of edges.

**Rendering in the Design view**
- Once extruded, a part was hard to read: both lights of the object
shaders sit near the camera, so the sides of a part came out in nearly
the same tone, and nothing marked where one face ends.
- The phong shader gains a studio lighting model, selected by a new
`lighting_model` uniform: a sky/ground hemisphere in world space, a key
light from the upper left and a weak fill, a plastic-like highlight, and
a darker base with a faint sheen toward the silhouette.
`GLCanvas3D::set_studio_lighting()` enables it per canvas; only the
Design canvas does. Every canvas sets the uniform on each use (0 for the
slicer's canvases), so Prepare and Preview render exactly as before.
- Every B-rep edge of a body is drawn as a 2 px dark line, depth tested
and pulled a few pixels toward the eye, so it hides behind the faces in
front. Seams of closed surfaces and degenerate edges are left out
(`GeometryEngine::display_edges`).

**Crash fix (Linux AppImage)**
- Drawing anything in a sketch crashed the AppImage: the constraint list
labels were formatted from narrow literals holding `—`, `·` and `°`;
`AppRun` sets `LC_ALL=C`, so wx's conversion returned NULL and
`wxString::Format` dereferenced it. They now go through
`wxString::FromUTF8`, as the panel's other non-ASCII literals already
do.

Docs updated to describe the design as it now stands: `design_tab.md`,
`interaction-model.md` and the portability note.

No change to slicing, profiles or presets. Existing 3MF recipes load and
rebuild as before: the v3/v4/v5 fixture tests pass.

# Screenshots/Recordings/Graphs

None attached. The behaviour was checked on the fork's Linux tester
AppImages, driven under Xvfb (see Tests).

## Tests

- `libslic3r_tests` on this branch, rebased on `main` (dc0e26918): 943
of 944 cases pass (1 skipped), 79,733 assertions. Linux, system OCCT.
- New or updated cases cover: the solver side and tangency fixes;
trim/offset/mirror on arcs and ellipses; body identity across history
edits; datum remapping, expressions, bindable fields; circular pattern,
hole standards, threads; the round trip of colours, origin and
auto-close; `body_touching_sketch`; multi-edge fillet/chamfer and its
save/load; the crossing/cusp loop analysis and the extrude refusal; the
Text parameters' save/load; `display_edges` on box, cylinder and cone;
revolve about a centerline, about the profile's own edge, refused
through the profile, stale axis index, axis save/load. The
truncated-recipe test accounts for every new tail field.
- All touched GUI translation units compile with GCC, and with clang
`-fsyntax-only` under the macOS job's warning flags. The modified
shaders pass `glslangValidator`.
- `gen_offer_table.py --check` passes, and `xgettext` + `msgfmt
--check-format` are clean on the CAD sources.
- On the fork's Linux tester AppImages (same code on `cad-mainline`),
driven headless:
  - drawing lines and rounded rectangles no longer crashes;
- Offset on one side of a rectangle selects and previews the whole
outline; Enter adds it;
- Text: the dialog opens at the top right, "Text N" appears in the tree
and the outline in the view while typing; double-clicking the feature
reopens it and a new string redraws in place;
- a block with a through hole and fillets: faces separate by
orientation, fillets shade round, edges are drawn and hidden behind the
part;
- a half-profile beside a construction centerline: Revolve preselects
the centerline, shows the axis dashed and builds the expected tube
(4000π mm³).
- **Not verified by hand:** multi-edge selection and the red loop
marking; Windows and macOS were not run.

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-10-05 18:58:21 +08:00
SoftFever 7495b9fda2 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-05 18:48:18 +08:00
SoftFever 3f844ee99c fix clang-tidy errors 2026-10-05 18:47:28 +08:00
HanifKoh 92de6f1e5b Restore Thread.hpp in MediaPlayCtrl.cpp for the Windows Non-Release Ping Test (#16155)
The only use of Slic3r::create_thread in this file sits behind
!BBL_RELEASE_TO_PUBLIC && __WINDOWS__, so the include looked unused in
every configuration CI builds. Windows Debug and RelWithDebInfo builds
failed without it.
2026-10-05 18:33:49 +08:00
SoftFever 257c589331 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-05 18:29:35 +08:00
HanifKoh c8edb29ddc Gate Pull Requests on clang-tidy Missing-Include Checks (#16154)
A Linux job configures without the precompiled header and runs clang-tidy over the C++ lines a pull request changes. The only check for now is misc-include-cleaner for missing includes; .clang-tidy is where further checks get enabled.

scripts/run_clang_tidy.sh (Linux, macOS) and scripts/run_clang_tidy.ps1 (Windows) run the same check locally: the same configure, the clang-tidy version pinned in scripts/clang_tidy_requirements.txt, and the same comparison against OrcaSlicer's main. They offer to install what is missing, or print the command to do it by hand.
2026-10-05 18:27:22 +08:00
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced

Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes.

clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer.

* Remove Unused Project Includes From Files With Platform-Specific Code

A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.

* Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass

The platform-file pass treated pchheader.hpp as an ordinary header and
emptied it, and left GUI_Preview.hpp and 14 other files relying on
headers they no longer reached directly.

* Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call

* Include Headers That Files Reached Through Ones the Cleanup Removed

* Drop Includes Duplicated by the Cleanup or by Main's Own Additions

* Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
2026-10-05 16:47:17 +08:00
SoftFever fa56a9cfeb Fix crash when returning to the Design tab after committing a body to the plate
The Design view drew sinking outlines by looking up its bodies in the
plate's model, reading past the end of an object's volumes once a body
was committed. The Design canvas no longer draws sinking outlines.
2026-10-05 16:22:52 +08:00
Ian Chua 86153752e6 test: OFL OTA for ARM64 test (TO BE REVERTED AFTER 2.5.0 ALPHA) (#16151)
Merged by /bot merge on behalf of @peachismomo (id 52488812).
Grants: resources/profiles/OrcaFilamentLibrary/filament/Elegoo, resources/profiles/OrcaFilamentLibrary.json, resources/profiles/Elegoo, resources/profiles/Elegoo.json
Head: 00276bf8ee
2026-10-05 08:19:40 +00:00
SoftFever e7545f0bc0 Commit Design bodies to the plate as one assembly, keeping their positions
Commit to Plate now sends all visible bodies to Prepare as one object with a
part per body, so their relative placement survives. A dropdown beside the
button switches to Commit to Plate (as bodies), the previous one-object-per-body
behaviour, and the choice is remembered.
2026-10-05 14:41:07 +08:00
HanifKoh 63d5fa23b6 Remove the OBJ Color Dialog That Texture Import Replaced (#16122)
* Remove the OBJ Color Dialog That Texture Import Replaced

* Remove references to ObjColorDialog
2026-10-05 14:39:10 +08:00
HanifKoh 1869895b0a Load One jQuery 3.6.0 Copy from include on Every Web Page (#16143)
Home, Project and seven setup-guide pages move from jQuery 2.1.1 to 3.6.0, and the four other copies are removed.
2026-10-05 14:32:38 +08:00
HanifKoh 48a8e33ec3 Delete Gizmo, SLA and Utils Files Nothing Builds or Includes (#16101)
GLGizmoSlaSupports, GLGizmoHollow, GLGizmoFaceDetector, GLGizmoText and GLGizmoAdvancedCut were already left out of the build, and GLGizmos.hpp, the only header including some of them, had no includers. VoxelizeCSGMesh.hpp uses types that no longer exist, SLA/bicubic.h does not compile, and Utils/ProfileDescription.hpp is included nowhere. Their CMake and gettext source-list entries go with them.
2026-10-05 14:26:15 +08:00
HanifKoh 2dbfc6bc5b Add Missing Includes to Tests Merged Since the Last Include Sweep (#16146) 2026-10-05 14:19:47 +08:00
HanifKoh 017bf0a2c8 Remove Unused Web Resources: Duplicate Swiper and jQuery, Test Data and Images (#16119)
* Trim the Bundled Swiper Library to the Files the Web Pages Load

* Remove Unused jQuery Copies, Model Test Data and Unreferenced Web Images
2026-10-05 14:16:59 +08:00
HanifKoh c44f2d6324 Remove the Unreachable MakerWorld Publish Dialog (#16121) 2026-10-05 14:12:00 +08:00
SoftFever 19af2b79f7 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-05 11:43:06 +08:00
SoftFever d999a0bac9 Merge branch 'main' into pr/tommasobbianchi/16019 2026-10-05 10:58:44 +08:00
Kris Austin f3d0b8a553 ci: move macOS jobs off the retiring macos-14 runner (#16088) 2026-10-04 21:12:32 -03:00
TheLegendTubaGuy 4b4a261787 Fix small binary STLs failing to load as ASCII (#16130) 2026-10-04 18:23:40 -03:00
Damir Galeev fb529f8315 Fix startup freeze from synchronous scripts in the camera view (#16104) 2026-10-04 18:22:35 -03:00
TheLegendTubaGuy 93fca83122 Fix memory leak of gap fill paths in solid infill (#16137) 2026-10-04 22:13:17 +01:00
Ioannis Giannakas 7fd6e5fd72 Fix crash on macOS when OrcaSlicer is quit from the Dock, a logout or a restart (#16136)
Fix crash when quitting from the Dock, logout or restart on macOS
2026-10-04 21:54:47 +01:00
Ioannis Giannakas b29c3b36ec Fix a small memory leak when creating default enum list options (#16133)
Fix memory leak in ConfigOptionDef::create_default_option for enum lists
2026-10-04 20:08:42 +01:00
SoftFever 4cb6ca7ecb Let Esc and a click on empty space deselect Design feature and body rows 2026-10-05 02:50:38 +08:00
SoftFever a29e3078e8 Keep automatic Design body colours clear of the selection colour 2026-10-05 02:50:21 +08:00
SoftFever 073e2d9c44 Highlight the faces a selected feature made in the Design tab
Selections are drawn as opaque faces in the selection colour with a cased outline instead of a
translucent tint over the body, so they read on a body of any colour. Selecting a Feature tree
row lights the faces that feature made rather than its whole body, which also makes fillet and
chamfer rows highlight again.
2026-10-05 02:50:14 +08:00
Damir GaleevandIan Bassi b6d11b2b3a Precise Seam: remove known limitations and rework perimeter intersection (#16072)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-10-04 14:47:56 -03:00
SoftFever be72afc9f7 Fix hidden features refusing to show again after their body's feature was hidden 2026-10-04 22:21:06 +08:00
SoftFever 1f91fdf637 Show feature and body actions on their rows in the Design sidebar
Each Feature tree row now carries Edit, Show/hide and Delete, and each
Bodies row carries Move, Show/hide and Delete. These act on that row
instead of on the selection. The eye shows a closed eye when the item
is hidden. Every clickable icon in the sidebar now highlights on hover.

Rename and Color now come first in the right-click menu, as items of
their own instead of inside Modify. This applies wherever they appear,
whether opened from the viewport or from a Bodies row.

Clicking an already-selected row no longer starts a rename; use F2,
the row's right-click menu, or the Rename command.
2026-10-04 21:27:12 +08:00
SoftFever 1d06b8576b Fix Design tab getting stuck unable to start a new sketch
After undo, redo, New Design, a project load, a delete or a reorder, a sketch
profile picked beforehand stayed selected even though it was gone. The
right-click menu then offered "Sketch profile" over an empty design with Create
greyed out. Those operations now clear the selection.

The Confirm button no longer stays greyed in a sketch session after a card whose
preview was invalid has been closed.

Delete and reorder now wait while a sketch or constrain session, the Text dialog,
an Insert placement or the move gizmo is open, instead of editing whichever
feature took its place in the list.
2026-10-04 00:01:05 +08:00
SoftFever 25f755c434 Open the Design tab sidebar at Prepare's width instead of its minimum 2026-10-03 21:01:56 +08:00
SoftFever d47fb804c3 Make the Design tab sidebar dockable and collapsible like Prepare's
The sidebar can move to either side, float, be resized, and collapse with
the canvas button or Shift+Tab. Its layout is remembered separately from
Prepare's, starts where Prepare's sidebar is, and View > Reset Window
Layout resets both tabs.
2026-10-03 19:05:53 +08:00
SoftFever a9c8721380 Add a build-large-panels-hidden rule to the orca-wxwidgets skill 2026-10-03 19:05:53 +08:00
Claude 815716a4b5 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream
Conflicts were only in include lists (CadDocument.cpp, SketchEngine.cpp);
both sides' includes are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-10-03 08:12:47 +00:00
SoftFever 38df8022e3 Open the Design tab quickly on Windows the first time
A page built on first click is now built before it is shown, as the idle
prebuild already does, so its controls are not created inside a visible
window. This takes the Design tab's first open on Windows from ~9 s to ~1 s.
2026-10-03 12:38:47 +08:00
SoftFever 9351bbaa83 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-03 02:30:59 +08:00
SoftFever 6a90946294 Keep the sketch value field on top of the dimension labels
Dimension labels drawn after the value field each lifted their own
window to the front, so a label could print over the number being typed.
2026-10-02 01:11:04 +08:00
Claude 410399fdb5 Design tab: follow Orca's theme, scale, mouse settings, dialogs and undo
Felix14-v2's review of OrcaSlicer#16019 found the tab behaving as a world of its own.

- Icons: the design_* glyphs were drawn in a fixed light grey, made for the dark ribbon, and the
  toolbar re-tinted some of them by rebuilding the bitmap from a wxImage, which drops the HiDPI
  scale factor Orca sets on Windows: at 150 % the icons came out half again too large for buttons
  that were sized in raw pixels, overlapping and clipped. The glyphs now use Orca's sidebar icon
  grey (#949494), which the icon cache maps per theme, nothing is re-tinted, toolbar glyphs drawn
  for Prepare's light toolbar use their "_dark" twin, and every size is in DIP.
- Theme: the chrome colours were read once, at construction, and nothing in the tab answered a
  theme switch, so switching left light surfaces and unreadable text in a dark tab and the other
  way round. The colours are now {light, dark} token pairs; MainFrame::on_sys_color_changed
  reaches DesignPanel::on_sys_color_changed, which moves every token colour onto the other
  theme's, runs the app's dark pass and re-rasterises the icons. Card borders are StateColors,
  resolved at paint time.
- Scale: MainFrame::on_dpi_changed reaches DesignPanel::msw_rescale, which re-rasterises every
  icon (buttons, flyout rows, card headers, the tree's image list, now sized from its bitmaps)
  and re-measures the Orca widgets.
- Mouse: the canvas no longer forces middle-drag to orbit and right-drag to pan; it reads the
  drag actions in Preferences > Control like Prepare. Left-drag is shared with picking, so the
  whole-body rubber band takes Shift+left-drag while left-drag is given to the camera.
- FPS counter: ImGui's display size is shared and only refreshed when a canvas sees its own
  size change; the Design canvas now re-announces its size when the tab is entered (and the
  editor canvas when it is left), as Plater does between Prepare and Preview.
- Viewport text: the status line and the tool readout were top-level popups over GL. A popup
  does not follow its frame, so the empty-canvas hint floated over other applications, and the
  readout was never taken down with the tab. Both are drawn by the canvas in the tool's ImGui
  pass now, with the theme's overlay style.
- Dialogs: messages use MessageDialog/RichMessageDialog; Add/Edit Variable is one Orca dialog
  with Name and Expression fields instead of two native text prompts; the Text dialog uses
  TextInput, ComboBox and CheckBox (its height is a TextInput: SpinInput is integer-only), and
  enumerates the installed fonts once per session. The ribbon's Confirm/Cancel, the reference
  pick buttons and the expression buttons are Orca Buttons; the variable actions are icon
  buttons like the other cards'.
- Undo: the tab's own Undo/Redo buttons are gone. The top bar's Undo/Redo drive the Design
  history while the tab is shown, greyed to what an undo would do, as Ctrl+Z and Edit already
  did.
- The first build of the tab logs how long each phase took ("Design tab build: ..."): it is
  under a second here but was reported at about fifteen on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-10-01 15:38:24 +00:00
SoftFever 96bf754af9 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-01 21:57:57 +08:00
SoftFever 377ebf2a03 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-01 14:51:35 +08:00
Claude 5254007a10 Merge main into the Design tab follow-up
main moved the CAD docs into docs/HLSD/design-tab.md and the offer
generator and atlas into scripts/CAD/ (#15803).

- The four docs this branch had edited are deleted as on main. What the
  branch changes about the design goes into the HLSD doc: the right-click
  is judged by drift over the whole press, with no time budget; a Text
  feature stores its outlines as well as its string, font and height; a
  new section on how the Design canvas renders bodies (studio lighting
  through the shared phong shader, B-rep edge ribbons).
- gen_offer_table.py keeps both main's atlas validation and this
  branch's L() markers on user-facing strings.
- The Text verb's hint is now changed in scripts/CAD/tool_atlas.json as
  well, so `gen_offer_table.py --check` passes.
- DesignPanel.cpp keeps the DesignTextDialog include and main's new
  path in the offer comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 18:00:46 +00:00
Claude d33dfa8f2c Tests: keep CAD test names ASCII so Windows CTest can select them
OrcaSlicer#16019's Windows unit-test jobs failed on one test, "Hole
standards: ... 82° countersink": CTest passes the test name to Catch on
the command line, the ° arrives in the ANSI code page, the filter
matches no test and the run counts as failed ("No test cases matched",
"No tests ran"). The name now says "82 degree"; it was the only test
name in the suite outside ASCII.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 12:29:39 +00:00
Claude da76c3f43f Design tab: Offset does not name a variable near
The Windows builds of OrcaSlicer#16019 failed in DesignSketchTool.cpp:
windows.h defines near (and far) as empty macros, so
`Vec2d near = m_op_anchor;` reads as `Vec2d = m_op_anchor;` under
clang-cl. The variable is now called closest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 11:23:12 +00:00
Claude 1ff8fc465c Design tab: body edges 2 px wide
Seen on the rig: at 1.5 px the body edges read as hairlines next to the
sketch and selection strokes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:46 +00:00
Claude a4387e93f2 Design tab: revolve about a line of the sketch
Reported: the Revolve axis could only be the sketch plane's X or Y axis
through its origin, so a half-profile drawn beside a centerline, the
usual way, could not be revolved about that centerline.

- CadFeature::revolve_axis_entity names a Line of the profile sketch to
  revolve about (a construction centerline, or an edge of the profile
  itself); -1 keeps revolve_axis. Appended at the end of the framed
  recipe, so existing projects load and rebuild unchanged. Revolve and
  Surface Revolve resolve their axis in one place (revolve_axis_of); an
  index that no longer names a line fails with a reason.
- SketchEngine::make_revolve takes the world axis. A profile with points
  on both sides of it is refused with "the profile crosses the revolve
  axis"; MakeRevol failed there with no reason.
- The Axis list of both cards reads Plane X, Plane Y, then every line of
  the sketch, named as the constraint list names them (Centerline E4,
  Line E3). A fresh revolve preselects the sketch's centerline when it
  has exactly one. The gizmo turns about the chosen axis and draws it
  dashed; construction lines no longer pull its centre.

Tests: a rectangle beside a construction centerline revolves into the
tube of the expected volume along that line; about its own edge, into a
cylinder; an axis through it is refused with the reason; a stale axis
index fails with a reason; the axis survives save and load. The
truncated-recipe test accounts for the new tail field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:46 +00:00
Claude b8d45c6bb1 Design tab: studio lighting and body edges in the 3D view
Reported: once extruded, a part is hard to read; the lighting says
little about its shape. Both lights of the object shaders sit near the
camera, so the sides of a part come out in almost the same tone, and
nothing marks where one face ends and the next begins.

- The phong shader gains a studio lighting model, chosen by a new
  lighting_model uniform: a sky/ground hemisphere in world space (up
  faces cool and bright, down faces warm and dark), a key light from the
  upper left and a weak fill from the right, a plastic-like highlight,
  and a darker base with a faint sheen toward the silhouette so curved
  faces read as round. GLCanvas3D::set_studio_lighting() makes a canvas
  draw its objects with it whatever the realistic-view preferences; only
  the Design canvas turns it on. Every canvas sets the uniform on each
  use, 0 for the slicer's, so they render as before.
- Every B-rep edge of a body is drawn as a thin dark line over it, depth
  tested and pulled a few pixels toward the eye so it wins against the
  faces meeting at it and hides behind the faces in front. The seam of a
  closed surface and degenerate edges are left out
  (GeometryEngine::display_edges). Edges are sampled once per shape and
  kept across recomputes that leave a body unchanged; bodies faded by
  body focus get fainter edges, and a dress-up previewing its result
  alone hides them with the bodies.

Tests: display_edges gives a box its 12 edges at their lengths, a
cylinder its two round rims without the seam, a cone its base rim
without the seam or the apex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:46 +00:00
Claude b060101707 Design tab: Text verb hint describes the Text feature
The context menu still said the text outline is added to the sketch as
editable lines, which is what it did before Text became its own
feature. Only SVG goes into the open sketch now; the comments on that
path say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:34 +00:00
Claude 5d4622c822 Design tab: Text is its own feature, previewed in the view and editable
Reported on the rig: Text showed no preview where it would go, its
dialog was pinned over the middle of the window (GNOME attaches a modal
dialog to its parent and it cannot be moved), and once confirmed the
text could not be edited and did not appear in the feature tree. Inside
an open sketch it became loose lines of that sketch.

- Text is always a feature, "Text N" in the tree. A new text goes on the
  plane of the open sketch (committed first when it holds anything,
  closed when it is empty), else centred on the picked face, else on the
  reference plane.
- The dialog is modeless and opens at the top right of the window. The
  feature is created at the first character and redrawn on every change,
  so the text appears in the view where it will be as it is typed. Enter
  inserts it, then the usual move/scale gizmo and Confirm; Esc, Cancel or
  closing the dialog takes it out again (undo to the checkpoint taken
  when it appeared).
- CadFeature keeps text_string, text_font (the WxFontUtils descriptor)
  and text_height, appended at the end of the framed recipe. Editing a
  Text feature reopens the dialog with them and redraws the outline in
  place, keeping its placement. The outlines are still saved, so the
  project opens the same on a machine without that font.
- The MCP control refuses writes while the Text dialog is open.

Tests: the text parameters survive a save and load along with the
outline; the truncated-recipe test accounts for the new tail fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:34 +00:00
Claude 4983796970 Design tab: sketch Offset takes the whole outline, not one segment
Offset worked on the single entity under the pointer. On an outline made
of many short entities (a text glyph, an imported shape) that is a
segment a few tenths of a millimetre long, and the starting distance was
a tenth of that: the ghost was too small to see, and a typed distance
moved one invisible segment. It read as "Offset does nothing, no
preview".

- The pick takes the chain the entity belongs to (connected_loop, same
  construction state), highlights all of it, and offsets it as one
  outline through offset_entities, which already joins and trims chains
  at their seams.
- The starting distance is a twentieth of the outline's size, so the
  preview is visible at once.
- The arrow reads its side off the ghost, since the engine may walk the
  picked entity backwards in the chain; it keeps pointing at the offset
  copy, flipped for a negative distance.
- A single entity still gets its Parallel/Concentric constraint. A chain's
  offset is placed as geometry, since its joined entities no longer map
  one to one onto the originals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:33 +00:00
Claude c664a24f4b Design tab: a closed loop that crosses or folds back is not a region
A sketch drawn on the rig extruded to walls with no caps. Every joint of
its loop met, so the loop analysis called it closed and MakeFace
accepted it, but the loop crossed itself: an arc left the top line's end
heading back over it and crossed it again 2.5 mm on. A second arc left a
0.28 mm line tangent to it but the other way, a cusp. The prism of that
face is an invalid solid, and it was shipped as a body.

- SketchEngine::wires_to_face checks the face it builds and, when OCCT
  calls it invalid, fails with "the profile crosses or folds back on
  itself, so it does not bound one region". The extrude reports that
  instead of producing the broken body.
- sketch_loop_defect() judges a closed loop of lines and arcs exactly:
  any contact between two of its entities away from the joints they
  share, or a joint where the curve turns straight back (a cusp; OCCT
  still builds that one, but it is never what was meant). It returns the
  point.
- The sketch uses it on every region: the loop is tinted red, the point
  gets a marker, and the status line says what the red means the first
  time one appears. The MCP loop report lists the defects and no longer
  calls such a profile buildable.

Tests: the rig's profile, with each defect and with both, from a
recording of the real entities. The analysis names the cusp at its joint
and the crossing on the top line, in either traversal order, and
passes ordinary tangent and collinear joints. The extrude refuses every
crossing variant with the reason, and the same arcs swept the other way
round extrude to a valid solid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:33 +00:00
Claude 3c218591bc Design tab: pick several solid edges and dress them in one feature
A solid edge could only be picked one at a time, and Fillet/Chamfer took
either that one edge or a whole face group. Rounding three chosen edges
meant three features, whose edge ids each resolve against a body the
previous one had already changed.

- Shift+click (or Ctrl+click) on an edge of the body already picked adds
  it to the selection, or removes it; the same modifiers that extend a
  sketch selection. The whole set is highlighted. A plain click replaces
  it, as before.
- Fillet/Chamfer dresses every picked edge in ONE feature at one size,
  all ids resolved against the same body. The card says "3 edges", the
  status line and the offer header name the count.
- CadFeature gains dressup_edges, appended at the end of the framed
  recipe, so existing projects load and rebuild unchanged. dressup_edge
  keeps the first edge, so an older build opening a newer project still
  dresses that edge instead of falling back to the face group.
- The MCP fillet/chamfer verbs take `edge` as one id or an array.

Tests: a fillet on the four picked top edges equals the Top face group
exactly; the list survives save/load; two opposite chamfers remove
exactly twice one; a missing id fails with a reason. The truncated-
recipe test accounts for the new tail field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:33 +00:00
Claude f4cdeb1706 Design tab: build constraint labels from UTF-8, not the C locale
Drawing a line or a rounded rectangle crashed the Linux AppImage: the
constraint list labels were formatted from narrow literals holding "—",
"·" and "°". wx converts a bare char* with the current locale, and the
AppImage's AppRun sets LC_ALL=C, so the conversion failed, the format
string came out NULL and wxString::Format dereferenced it
(wxFormatConverterBase<wchar_t>::Convert, from constraint_label via
rebuild_constraint_list). A build started under a UTF-8 locale never
showed it.

These literals, and the "…" of the interference report, now go through
wxString::FromUTF8, as the panel's other non-ASCII literals already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:32 +00:00
Claude ae1dc95756 CAD: catch OCCT failures in body_touching_sketch, Standard_Failure first
The extrude default query must never throw; on OCCT >= 8 Standard_Failure
derives from std::exception, so its handler comes first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:25 +00:00
Claude 205bc1b332 Design tab: Text dialog with font, height and live outline; offer header
- Text opens a dialog instead of a bare text entry: any installed font
  (bold, italic), the height in mm, and a live outline of exactly what
  will be inserted with its size. Enter inserts, Esc cancels; the last
  font and height are remembered. text_to_regions gains an overload
  taking a loaded font.
- The offer menu opens with a greyed title naming what the rows act on
  ("Flat face 4 of Body 2", "Sketch line", "Nothing selected").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:25 +00:00
Claude c74ddf8bb5 Design tab: translatable offer, one vocabulary, reports in the status line
- Offer table: user-facing strings carry the L() marker so xgettext
  extracts them; DesignOffer.hpp, DesignSketchTool.cpp and
  SketchInlineEditor.cpp are listed in localization/i18n/list.txt.
- Offer: model-mode Constrain sits in the same row as the sketch one;
  Interference is wired; Rib shows its R key; a verb that accepts the
  selection but is blocked by the document stays greyed with its reason
  instead of vanishing from the submenu; one refusal wording per verb.
- Extrude infers Join when the profile touches a solid (new
  CadDocument::body_touching_sketch) and on face push/pull; New body in
  free space. Revolve/Sweep/Loft/Boolean use the same result words.
- Interference and volume/area reports go to the status line in mm3/mm2
  instead of modal dialogs; Delete Body no longer asks (it is undoable).
- New feature names match the card header ("Extrude 3"), translated;
  "Coordinate system", "Angle (°)", center/color spelling, translated
  face and length readouts, slot hints say width.
- CAD gizmos in Prepare are selectable only with the CAD feature on; the
  sketch auto-close setting is stored per design.
- Docs: confirm/cancel rules, enabling the feature and MCP in
  design_tab.md; drift-only right-click in interaction-model.md; the
  portability note rewritten to describe the integration as it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:25 +00:00
Claude 829124982d Design tab: one rule for mouse, Enter and Esc; honest status messages
Keyboard and mouse
- Esc drops what is pending (picks, a dimension's first point, an edit-op or transform) and
  never applies it; Enter applies a ready edit-op/transform, ends a polyline/spline chain,
  ends an armed tool, and confirms a feature card exactly when its ✓ is enabled.
- Right-click only abandons the gesture in progress; with nothing pending it opens the offer
  in every tool (Trim, edit-ops, transforms, Dimension, Constrain, TransformArt, move gizmo).
  Clicking empty space no longer commits. The offer needs no timing, only a still press.
- Delete removes only an explicit selection. Undo/redo inside a sketch go through the same
  route as the buttons (whole shapes, with redo); Edit > Undo follows the shown tab.
- The canvas no longer handles Delete/Esc/Ctrl+Z itself (Backspace in a value field deleted
  the geometry it measured); F is in the panel's key map.
- Value fields: a refused value keeps the field open with the reason; click outside and Tab
  commit; an untouched field commits the exact value; any decimal separator is accepted;
  lengths are always mm; validation is the same for every editor.
- Snapping: the marker shows only where the click will actually snap; pick tolerances are
  one set of pixel budgets (Constrain picks within reach; no mm floor on labels).

Messages and consistency
- set_status(kind, text) gives every status line its own colour and glyph; kernel errors are
  translated into sentences and formatted, not concatenated; sketch refusals go to the status
  line instead of the per-frame HUD that erased them.
- Hints describe the gestures that now work; Dimension shows its second step; Constrain uses
  the sketch palette (red means conflict only); the straight slot's value is its width.
- Hole/Thread/Project keep the user's pick or refuse up front; circular pattern opens with its
  own preview; thread fields use the nominal diameter and the ISO internal depth.

Integration
- MCP loads the project's recipe before touching the document, refuses to mutate it while the
  tab is busy, never runs a request that already timed out, only replaces a socket at its
  path, caps line length and removes the socket at exit; not started in the G-code viewer.
- Hiding a feature keeps later body references on their bodies; a design keeps its modeling
  origin across printer changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:25 +00:00
Claude a043979f44 CAD kernel: fix the sketch solver, trim/offset/mirror, and history references
Sketch layer
- Trim/extend of an arc (and a circle opened into an arc) rewrites p0/p1 from the new angles;
  the wire builder, the solver and snapping read them.
- Tangency binds the arc end that touches the line (or the other arc) instead of always the
  start, so a fillet tangent to both legs solves; circle-circle / circle-arc tangency uses the
  centre distance instead of CURVE_CURVE_TANGENT, which aborts on a circle.
- Point-on-line distances and circle-line tangency keep the side the geometry is on; a point
  on an arc's rim uses PT_ON_CIRCLE.
- The partitioned solve keeps constraints onto the origin/axes and counts free entities' DOF.
- Zero-radius circles get no solver primitive and build no wire; constraints the solver cannot
  apply are reported in SketchSolveResult::skipped.
- EllipseArc: mirror no longer yields the complement; after a solve its angles and ends are
  re-derived; on the XZ plane it is no longer built mirrored.
- Offset: a circle follows the "+d = left of travel" rule (it shrinks, like a CCW arc chain);
  chains are joined at the weld tolerance. Bridge end pole fixed (G1, no cusp). Negative-scale
  transforms keep arcs and ellipses on their ends. Inference tolerances aligned with the weld.

Model layer
- Body references follow the body across delete / reorder / hide (resolved by the feature
  that made it); datum-plane ordinals are re-pointed; an index past the end is an error, not
  "the last body". New set_feature_enabled(). A move that puts a consumer above its input is
  refused.
- Threads made from now on read thread_radius as the nominal major radius (internal: bore to
  minor, groove to major; external: groove cut into the rod); older recipes build as before.
  Bad thread parameters say why. Circular patterns span their angle end to end (new ones);
  add_pattern pivots on the modeling origin.
- clear() drops variables; expression fields the GUI offers are bindable (thread_diameter,
  helix_*, thicken_thickness, ...); deg()/rad() in expressions; the recipe saves the modeling
  origin and body colours; names/colours follow bodies by identity.
- Boolean and dress-up failures throw instead of returning the input; one produces_body();
  hole standards corrected (82° inch countersinks, UNC names, #10-24); legacy profile solve
  validates indices and writes back only on success; v4 recipes read with a frozen field list.

Tests cover each of the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-09-30 08:34:25 +00:00
Tommaso Bianchi 73bfcccd50 The sketch tools stop lying about what they did
Twelve defects found by the 2D design pass.

- ONE erase path repairs the dimensions' cached constraint indices. Removing a
  constraint renumbered m_constraints and left every later DimAnnot.con pointing
  one slot short, and set_dimension_value checked only the range, not the slot's
  identity — so editing a dimension's value could overwrite an unrelated
  constraint. All five mutators (badge delete, fillet/chamfer, Move/Rotate/Scale,
  the two drop paths) now route through erase_constraints().
- apply_dimension validates BEFORE moving or recording. A value outside a case's
  threshold moved nothing and then recorded the constraint anyway, handing the
  solver a number it could never satisfy; the socket guarded against this, the
  tool did not.
- A Distance and its zero case are one dimension slot: typing 0 and then 5 used
  to leave a Coincident AND a Distance on the same operands.
- A dimension whose constraint the solver rejected is named: the label renders in
  the refusal colour and the commit says the sketch is over-constrained, instead
  of showing a number the geometry does not have.
- Six silent refusals now speak: fillet/chamfer on a non-corner and on an
  overrunning radius, offset on an ellipse or spline (the kernel's own reason),
  a rejected array binding ladder, a dimension pick on an unsupported entity.
- set_tool commits a ready transform instead of dropping it, the rule the ready
  edit-op already followed.
- Constraint releases are reported, and roles_of is one function again (the two
  copies had already diverged on EllipseArc).
- Labels no longer collide: every label is its own centred ImGui window at an
  anchor whose offsets are multiples of the text height, so on a feature smaller
  than one text height a line's Length and Angle labels landed on the same spot.
  draw_text, the one function all of them pass through, now pushes a colliding
  label clear of the ones already drawn this frame.

Verified: build and LTO link on behemoth (exit 0, new binary); on the rig,
badge-delete took constraints 3->2 with dof 9->10 and the geometry untouched;
the draw-then-edit chain committed a typed 70 to exactly 70.0 mm; and a 5.4 mm
selected line renders "5.4 mm" and "21.8°" as separate readable labels.
NOT yet exercised: the stale-index corruption itself (needs a middle delete with
a labelled dimension after it), the poison-value guard through the field, the
transform commit, and the red refusal label.
2026-09-30 08:34:25 +00:00
Tommaso Bianchi c7700acc95 The 2D sketcher tool architecture, batches 9-14
The design pass over the rest of the 2D tool suite: Select/Measure,
Dimensioning, Constraints, Modification (Trim/Extend/Split/Offset/Mirror),
Dress-up (Fillet/Chamfer) and Transform, each with its FSM, its event routing,
its C++20 blueprint and the defects it exposed.

Thirty-two findings, every one verified against the tree with file:line, plus
nineteen open scope calls and a closing note that orders the six cross-cutting
changes by what unblocks what. Batches 1-8 were delivered in-session; only
their cross-cutting results (six archetypes, two defect classes) survive here.
2026-09-30 08:34:24 +00:00
Tommaso Bianchi 54b3e6c7da Include GUI.hpp where into_u8 is used (the upstream sync dropped the transitive path) 2026-09-30 08:34:24 +00:00
Tommaso Bianchi 812063d085 The label under an open value field is not drawn twice 2026-09-30 08:34:24 +00:00
1470 changed files with 16395 additions and 114113 deletions
+7 -3
View File
@@ -1,7 +1,11 @@
# clang-tidy configuration. Only missing includes are reported for now: a file
# should include the header for every symbol it uses, not rely on the
# precompiled header or another header's includes. Run with --fix to add them.
# clang-tidy configuration, enforced by the clang-tidy CI job on the lines a pull
# request changes (scripts/clang_tidy_diff.py). Only missing includes are reported
# for now: a file should include the header for every symbol it uses, not rely on
# the precompiled header or another header's includes. Run with --fix to add them.
# Every check listed here gates pull requests, so enable a new one only once the
# code it flags on touched lines is reasonable to fix in passing.
Checks: '-*,misc-include-cleaner'
WarningsAsErrors: '*'
CheckOptions:
# Missing includes only. Builds without the precompiled header break on these.
misc-include-cleaner.UnusedIncludes: false
@@ -405,9 +405,13 @@ page object to insert and remove by pointer). `LazyPage(parent, name, order, fac
factory is `new Panel(parent)`. Its `Show(true)` builds the panel the first time (only once the
top-level frame is shown — `MainFrame::Show` completes the start page on the frame's first show) and
forwards later shows/hides to the panel, so the panel's own `Show()` override stays its activation
hook. A panel built while its page is hidden stays hidden, and `when_built` gives it the dark-UI pass
the frame ran before it existed (`apply_dark_ui_to_lazy_panel`). `pending()` is true only while the page
is in the book.
hook. The build runs before the placeholder's own `wxPanel::Show(true)`, so an on-demand build creates
its controls in a hidden window as a prebuild does: on MSW each control created or moved inside a
shown window re-clips and repaints its shown siblings, which made a large panel's first show take
seconds. A lazy panel's constructor therefore runs off screen (except the start page's) and must not
rely on `IsShownOnScreen()`. A panel built while its page is hidden stays hidden, and `when_built`
gives it the dark-UI pass the frame ran before it existed (`apply_dark_ui_to_lazy_panel`).
`pending()` is true only while the page is in the book.
### Staged construction: StagedBuild
@@ -641,8 +645,10 @@ Spacing constants come from `SidebarProps` (`Plater.hpp`): `TitlebarMargin()`, `
### Docking
`Plater::priv` owns `AuiMgr m_aui_mgr` (a `wxAuiManager` subclass whose `CreateFloatingFrame` returns a
themed `FloatFrame : wxAuiFloatingFrame`), managing the plater. Panes: `"sidebar"` (left, no close
Docks are `AuiMgr`s (`AuiMgr.hpp`), a `wxAuiManager` subclass carrying Orca's dock art, theme and
Wayland rule (`references/webview-gl-aui-media.md`). `Plater::priv` owns `m_aui_mgr`, managing the
plater; the Design tab owns its own for its sidebar (`DesignPanel::m_aui`, layout in
`design_window_layout`). Plater panes: `"sidebar"` (left, no close
button, not top/bottom dockable), `"main"` (`CenterPane()`, the `panel_3d`), `"uv_editor"` (right,
hidden until the texture-displacement gizmo shows it), plus dynamic dock panes. The default perspective
is saved right after `AddPane`; the app-config `window_layout` is applied with
@@ -271,6 +271,14 @@ Each bullet names the mechanism and the file that owns it.
- **Painting:** in 3.3.2 windows are not double-buffered by default (the 3.3.0 global
`WS_EX_COMPOSITED` was reverted, `docs/changes.txt:308`). Custom widgets buffer by hand →
`references/painting-custom-widgets.md`.
- **Building large panels:** every control is a native child window, and outside a sizer pass a move
or resize is immediate, repainting when the window is shown [source: `src/msw/window.cpp:2036`
`DoMoveSibling` → `MSWMoveWindowToAnyPosition(..., IsShown())`]; `wxStaticText::SetLabel`/`SetFont`
resize the control that way (`src/common/stattextcmn.cpp:334` `AutoResizeIfNecessary`). Created
inside a shown parent, each control re-clips and erases its shown, overlapping siblings, so the
cost grows with the number already built and hundreds of controls take seconds. Build a large
panel while its parent is hidden and show it once complete — `LazyPage::Show` builds its panel
before showing the page for this reason → `references/orca-architecture.md` §Deferred construction.
- **Modal loops:** idle events do not run inside the Windows sizing/moving modal loop, so the 3D
canvas renders from `on_paint` on MSW (c06a0223a7) → `references/webview-gl-aui-media.md` §GLCanvas3D rendering.
- **Mouse capture:** `wxEVT_MOUSE_CAPTURE_LOST` and `wxEVT_MOUSE_CAPTURE_CHANGED` are delivered →
@@ -474,7 +482,7 @@ start there when looking for prior art. The sites below are exemplars, cited by
data-view cell editors an explicit top-level transient parent.
`DropDown::ShouldDismissOnTopWindowDeactivate` handles Wayland chains.
- `CheckBox::CheckBox`, `SwitchButton`, `RadioBox`, `ScalableButton` (`wxExtensions.cpp`),
`ObjColorDialog`, `PresetComboBoxes` call `RemoveButtonBorder`; `TextInput` and `SpinInput` call
`PresetComboBoxes` call `RemoveButtonBorder`; `TextInput` and `SpinInput` call
`RemoveInputBorder` on their inner `wxTextCtrl`.
- `Plater::priv::priv` together with `sanitize_window_layout_for_wayland`: AUI floating is disabled on
Wayland.
@@ -812,7 +820,7 @@ widgets do it in their constructors.
(`"*.GtkBitmapToggleButton"`, class `"GtkEntry"`). The first call changes every matching widget in
the process, not just the one passed in.
**Callers:** `CheckBox::CheckBox`, `SwitchButton`, `RadioBox`, `ScalableButton`, `ObjColorDialog` and
**Callers:** `CheckBox::CheckBox`, `SwitchButton`, `RadioBox`, `ScalableButton` and
`PresetComboBoxes` (`RemoveButtonBorder`); the inner `wxTextCtrl` of `TextInput` and `SpinInput`
(`RemoveInputBorder`). A new owner-drawn control built on a native GTK widget needs the same call.
@@ -88,6 +88,7 @@ available; GTK2/WebKit1 is an opt-out configuration the GUI does not support.
24. ImGui text uses `_u8L`; ImGui sizes are physical pixels (scale by `GLCanvas3D::get_scale()`);
a font atlas must fit `GL_MAX_TEXTURE_SIZE`. §[ImGui boundary](#the-imgui-layer-boundary)
25. After `AddPane` or any `wxAuiPaneInfo` change, call `wxAuiManager::Update()` once for the batch.
Keep a docked pane hidden until the managed window has been laid out.
§[AUI](#wxauimanager-and-orcas-docking)
26. Give every pane a unique, stable `Name()` free of layout delimiters (`|`, `;`, `=`, `\`).
§[AUI](#wxauimanager-and-orcas-docking)
@@ -920,6 +921,16 @@ widgets beside the canvas (a sibling, not a child over it), or in a popup.
- **Update.** "Update() must be invoked after AddPane() or InsertPane() … any number of changes may be
made to wxAuiPaneInfo structures (retrieved with wxAuiManager::GetPane), but to realize the changes,
Update() must be called" (`interface/wx/aui/framemanager.h:747-756`). Batch, then `Update()` once.
- **Dock size** [source]. The docs limit "any new dock" to a `SetDockSizeConstraint` fraction of the
window (`interface/wx/aui/framemanager.h:660-672`). The source applies it to the managed window's client
size at the `Update()` that creates the dock, sizing the dock from its panes' `best_size` and then
raising it to their `min_size` (`src/aui/framemanager.cpp:2748-2848`); a dock with a resizable pane
keeps that size (`2674-2677`), and resizing the managed window only re-lays the docks out
(`4542-4546`). A pane first shown before its managed window is laid out — a `wxDefaultSize` window is
20×20 until its first sizer layout (`references/sizers-layout.md`) — docks at that width or its
`min_size` and stays there. Hiding a dock's last shown pane empties it, and the next `Update()`
removes it (`2733-2738`) with any `dock_size` a `LoadPerspective` restored, so the pane comes back
at its `best_size`.
- **Names** [source]. A duplicate `Name()` hits `wxFAIL` (silent in Orca); duplicate and empty names
get a random identifier (`src/aui/framemanager.cpp:949-1010`), which `LoadPerspective` can never
match. `AddPane` rejects a null window, but its "already managed" check looks up `paneInfo.window`
@@ -953,11 +964,12 @@ widgets beside the canvas (a sibling, not a child over it), or in a popup.
- `wxAuiNotebook` (not used by Orca): page indices are logical; use `GetPagePosition()` for on-screen
order (`docs/changes.txt:121-126`); the default tab art changed (`35-37`).
**OrcaSlicer.** `Plater::priv` owns `AuiMgr m_aui_mgr`, a `wxAuiManager` whose `CreateFloatingFrame`
returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`).
- Setup: `SetManagedWindow(q)` (the Plater), `SetDockSizeConstraint(1, 1)`; on Wayland clears
`wxAUI_MGR_ALLOW_FLOATING` (`disable_wayland_floating`); dock-art metrics set here, colours in the
dark-mode update (`Plater::priv::apply_color_mode`).
**OrcaSlicer.** Orca's docks use `AuiMgr` (`AuiMgr.hpp`), a `wxAuiManager` whose `CreateFloatingFrame`
returns a `wxAuiFloatingFrame` that applies `UpdateFrameDarkUI`. `init(window)` calls
`SetManagedWindow(window)` and `SetDockSizeConstraint(1, 1)`, clears `wxAUI_MGR_ALLOW_FLOATING` on
Wayland and sets the dock-art metrics and colours; `apply_color_mode()` re-colours captions, sashes and
borders and is called from the owner's theme-switch handler. `Plater::priv::m_aui_mgr` manages the
Plater (Prepare and Preview); `DesignPanel::m_aui` manages the Design tab's body below its toolbar.
- Panes: `"sidebar"` (left, no close button, not top/bottom dockable, `BestSize` in em units),
`"uv_editor"` (right, hidden; forced hidden again after a layout load), `"main"`
(`CenterPane().PaneBorder(false)`, holding `panel_3d` with View3D, Preview and AssembleView in one
@@ -966,8 +978,8 @@ returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`).
`window_layout` app-config string goes through `sanitize_window_layout_for_wayland` (strips
floating/floatable bits on Wayland) and `LoadPerspective(layout, false)`, falling back to the default
on failure; then one `Update()`. `Plater::priv::reset` saves `SavePerspective()` back to
`window_layout`. An idle handler copies the docked sidebar width into `BestSize` (wxAUI does not record
a dragged sash there), so the width persists. `Plater::priv::reset_window_layout` is the model for
`window_layout`. `AuiMgr::track_docked_size` copies a docked pane's size into `BestSize` on idle
(wxAUI does not record a dragged sash there), so the width persists. `Plater::priv::reset_window_layout` is the model for
re-establishing panes after a load: it loads the default perspective, then re-shows each plugin pane
the perspective does not list.
- Plugin panes: `Plater::add_dock_pane(window, name, caption, dock, size, on_close)` (window must be a
@@ -977,13 +989,29 @@ returns `FloatFrame : wxAuiFloatingFrame` (applies `UpdateFrameDarkUI`).
`Destroy()`s without running `on_close`; `show_dock_pane` toggles and updates. Pane names come from
`plugin_pane_name` (`DockPanel.cpp`), which replaces `|`, `;`, `=`, `\` — the perspective-string
delimiters.
- Other pages: a page outside the Plater docks through its own `AuiMgr` member, since the Plater's
manages only the Plater. Its managed window is a body panel below the page's toolbar; a sidebar
takes `AuiMgr::sidebar_pane_info()`; the layout has its own app-config key, is reset from
`Plater::reset_window_layout`, and is saved by a `shutdown()` called from `MainFrame::shutdown`,
which also detaches a floating pane (Lifetime below). Where floating is disabled it forces
`Dock().Floatable(false)` after loading, as plugin panes do. A `LazyPage` builds its panel at 20×20
and lays it out only as the tab shows, so the sidebar stays hidden until the tab is first shown (Dock
size above). `DesignPanel` is the model
(`docs/HLSD/design-tab.md`). A `GLCanvas3D` beside such a sidebar gets that sidebar's collapse
button with `set_collapse_toolbar`; without it the canvas falls back to the Plater's button, which
collapses Prepare's sidebar.
- Lifetime: no explicit `UnInit`. `m_aui_mgr` is a `Plater::priv` member, so it is destroyed in
`~Plater`, before `~wxWindow` destroys the Plater's children (`wxWindowBase::DestroyChildren`,
`src/common/wincmn.cpp:586-609`); a floating frame is such a child (`CreateFloatingFrame(m_frame, …)`,
`src/aui/framemanager.cpp:3151`), so a pane still floating at that point outlives the manager [source].
Plugin panes are removed earlier (`MainFrame::shutdown` → `Plater::remove_dock_panes`). The sidebar
`GetPane()` reference captured by the idle lambda is safe across `AddPane`; never `DetachPane` a pane
whose reference is captured.
Plugin panes are removed earlier (`MainFrame::shutdown` → `Plater::remove_dock_panes`). A handler
that can outlive its pane looks the pane up when it runs, as `track_docked_size` does: an idle
handler bound on the pane's window outlives the `DetachPane` in `DesignPanel::shutdown`. Never detach
a pane whose `GetPane()` reference is captured (Pane references above). `DesignPanel::shutdown` is
the model for a member manager: it detaches a
floating sidebar, which `Destroy()`s the frame into the pending-delete list, and the main frame's
deletion deletes pending top-level children before its own children (`~wxTopLevelWindowBase`,
`src/common/toplvcmn.cpp:67-93`), while the panel and its manager are still alive [source].
**Pitfalls.**
- **Rule:** Call `Update()` once after a batch of `AddPane`/`wxAuiPaneInfo` changes.
@@ -198,7 +198,7 @@ change, its line, and what it means for Orca.
| `wxAuiGenericTabArt` subclasses (also via `wxAuiMSWTabArt`) override `DrawPageTab()`/`GetPageTabSize()` instead of `DrawTab()`/`GetTabSize()`; direct `wxAuiTabArt` subclasses still work | :115-120 | none: no Orca tab art |
| `wxAuiNotebook` page index is logical (reorder-independent); `GetPagePosition()` gives the screen position | :122-126 | none; index math under `wxAUI_NB_TAB_MOVE` is what breaks |
| `wxListbook`/`wxChoicebook` interpret mnemonics in page titles "just as the other wx*book classes already did" | :128-130 | Orca uses neither (`BedShapeDialog` uses `wxSimplebook` + a combo); every book interprets `&` (`interface/wx/bookctrl.h:141-147`) → rule 11 |
| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :132-133 | Plater's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `Plater::priv::priv`), so the docking hint no longer fades; add the flag if wanted |
| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :132-133 | Orca's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `AuiMgr::init`), so the docking hint no longer fades; add the flag if wanted |
| `wxPrintDialogData::SetAllPages(false)`/`SetSelection(false)` changed meaning | :135-137 | none |
| `wxGetTranslation()` returns `wxString` by value | :139-142 | pitfall below |
| `wxWindow::Raise()` no longer shows a hidden window on any port | :144-146 | pitfall below; ba867cc534 |
+3 -3
View File
@@ -127,7 +127,7 @@ jobs:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
uses: ./.github/workflows/build_check_cache.yml
with:
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
arch: ${{ matrix.arch }}
build-deps-only: ${{ inputs.build-deps-only || false }}
force-build: ${{ github.event_name == 'schedule' }}
@@ -138,7 +138,7 @@ jobs:
if: ${{ !cancelled() && needs.build_macos_arch.result == 'success' && !inputs.build-deps-only && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
uses: ./.github/workflows/build_orca.yml
with:
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
arch: universal
macos-combine-only: true
secrets: inherit
@@ -182,7 +182,7 @@ jobs:
if: ${{ !cancelled() && success() }}
uses: ./.github/workflows/unit_tests.yml
with:
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
artifact: ${{ github.sha }}-tests-macos-arm64
test-dir: build/arm64/tests
# Slice a two-colour cube through every shipped printer, and through every
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
# Anything that changes how the tree is built belongs in the key, or a job
# restores one it cannot use. Linux amd64 passes no arch deliberately, so
# 'linux-clang' keeps the cache it already has.
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
cache-os: ${{ runner.os == 'macOS' && format('{0}-{1}', inputs.os, inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
# The Windows ARM64 deps build in build-arm64, all others under build;
# build_deps.yml and build_orca.yml pass the Windows directory to build_win.bat.
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || (runner.os == 'Windows' && inputs.arch == 'arm64') && '-arm64/OrcaSlicer_dep' || '/OrcaSlicer_dep' }}
+5 -3
View File
@@ -183,10 +183,12 @@ jobs:
- name: Free disk space
if: runner.os == 'macOS' && !inputs.macos-combine-only && !vars.SELF_HOSTED
run: |
df -hI /dev/disk3s1s1
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} +
df -hI /
# Keep only the selected Xcode
xcode=$(basename "$(cd "$(xcode-select -p)/../.." && pwd -P)")
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "$xcode" -exec rm -rf {} +
sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
df -hI /dev/disk3s1s1
df -hI /
- name: Build slicer mac
if: runner.os == 'macOS' && !inputs.macos-combine-only
+89
View File
@@ -0,0 +1,89 @@
name: clang-tidy
# Runs clang-tidy, with the checks in .clang-tidy, over the C++ lines a pull
# request changes (scripts/clang_tidy_diff.py). The compile database is
# configured without the precompiled header, so code that only builds because
# the PCH supplied an include fails here.
#
# No paths filter: the job is a required check, and a workflow skipped by a
# paths filter leaves a required check pending forever. A PR with no C++
# changes finishes after the first step.
on:
pull_request:
branches:
- main
- release/*
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
clang_tidy:
# Branch protection requires this check by name. Renaming the job disables
# that gate.
name: clang-tidy
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
lfs: 'false'
# The PR merge commit plus its first parent, the base it is diffed against.
fetch-depth: 2
- name: Look for changed C++ files
id: changes
run: |
if git diff --name-only HEAD^1 -- src tests | grep -qE '\.(cpp|cc|cxx|hpp|h|hxx)$'; then
echo "cpp=true" >> "$GITHUB_OUTPUT"
else
echo "No C++ changes under src/ or tests/."
fi
# Parsing needs the dependency headers, not a build of this PR's deps/, so
# a PR that changes deps/ falls back to the newest cache main has.
- name: Restore cached deps
if: steps.changes.outputs.cpp == 'true'
uses: actions/cache/restore@v6
with:
path: ${{ github.workspace }}/deps/build/OrcaSlicer_dep
key: linux-clang-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }}
restore-keys: linux-clang-cache-orcaslicer_deps-build-
fail-on-cache-miss: true
- name: Apt-Install Dependencies
if: steps.changes.outputs.cpp == 'true' && !vars.SELF_HOSTED
uses: ./.github/actions/apt-install-deps
- name: Install clang-tidy
if: steps.changes.outputs.cpp == 'true'
run: |
python3 -m venv "$RUNNER_TEMP/clang-tidy"
"$RUNNER_TEMP/clang-tidy/bin/pip" install --quiet -r scripts/clang_tidy_requirements.txt
# DEP_BUILD_DIR is named outright: CMake would otherwise derive it from the build
# directory's name and look for the dependencies in deps/build-tidy.
- name: Configure without the precompiled header
if: steps.changes.outputs.cpp == 'true'
run: >
cmake -S . -B build-tidy -G Ninja
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DSLIC3R_PCH=OFF -DORCA_TOOLS=ON -DBUILD_TESTS=ON
-DDEP_BUILD_DIR=${{ github.workspace }}/deps/build
# The one header the build generates rather than the configure.
- name: Generate git_commit_hash.h
if: steps.changes.outputs.cpp == 'true'
run: cmake --build build-tidy --target git_commit_hash_header
- name: Run clang-tidy on the changed lines
if: steps.changes.outputs.cpp == 'true'
run: >
python3 scripts/clang_tidy_diff.py -p build-tidy --base HEAD^1
--clang-tidy "$RUNNER_TEMP/clang-tidy/bin/clang-tidy" -j "$(nproc)"
+1
View File
@@ -36,6 +36,7 @@ ctest --test-dir ./tests/fff_print -C Release
- C++17, selective C++20. PascalCase classes, snake_case functions/variables
- `#pragma once` for headers. Smart pointers and RAII preferred
- Include what you use: include the header for every symbol a file uses, and keep headers compilable on their own. Never rely on the precompiled header or a transitive include. The `clang-tidy` CI job enforces this on changed lines; run the same check locally with `scripts/run_clang_tidy.sh` (`scripts\run_clang_tidy.ps1` on Windows), which sets up everything it needs
- Parallelization via TBB — be mindful of shared state
- Always use `SetSizerAndFit(sizer)` instead of `SetSizer(sizer)` on top level window. Unless `SetSizer` must be called before the full layout is built, call `sizer->SetSizeHints(window)` afterwards in this case.
+13
View File
@@ -75,6 +75,19 @@ static FILE *stl_open_count_facets(stl_file *stl, const char *file, unsigned int
break;
}
}
// Zero normals and coordinates like 10 or 15 have no byte above 127, so the test above can miss a binary file.
// Its size still matches its facet count; text read as that count would need a file of gigabytes.
if (stl->stats.type == ascii) {
uint32_t header_num_facets;
fseek(fp, custom_header_length, SEEK_SET);
if (fread(&header_num_facets, sizeof(uint32_t), 1, fp) == 1) {
#if BOOST_ENDIAN_BIG_BYTE
stl_internal_reverse_quads((char*)&header_num_facets, 4);
#endif /* BOOST_ENDIAN_BIG_BYTE */
if (header_size + uint64_t(header_num_facets) * SIZEOF_STL_FACET == file_size)
stl->stats.type = binary;
}
}
rewind(fp);
uint32_t num_facets = 0;
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -38,7 +38,11 @@ no wx dependency and is unit-tested.
The notebook needs a page object for a tab to exist and for tabs to be inserted and
removed by pointer, and the placeholder is that object. It builds the real panel inside
itself the first time it is shown and forwards showing and hiding afterwards, so a panel's
own show handling stays its activation hook. Nothing builds while the main window is
own show handling stays its activation hook. The build runs before the placeholder shows
itself, so a panel built on demand is created in a hidden window as a prebuilt one is: on
Windows every control created or moved inside a shown window re-clips and repaints its
shown siblings, which makes building a large panel into a shown page many times slower.
Nothing builds while the main window is
hidden; the window's first show builds the start page. A page that is out of the book is
not prebuilt. A panel built while its page is hidden stays hidden, and gets the theming
the window applied before the panel existed.
+112 -7
View File
@@ -13,8 +13,9 @@ known.
Its coupling to the rest of the application is deliberately narrow. It adds no stage to the
slicing pipeline and touches neither the preset system nor `Tab`. It reaches the rest of Orca
in two places: **Commit to Plate**, which hands finished solids to Prepare as ordinary model
objects, and one optional 3MF archive entry that carries the recipe. Everything else is
contained in `src/libslic3r/CAD/` and `src/slic3r/GUI/CAD/`.
objects (by default one assembly object with a part per body, which keeps the bodies' relative
placement, or one object per body), and one optional 3MF archive entry that carries the recipe.
Everything else is contained in `src/libslic3r/CAD/` and `src/slic3r/GUI/CAD/`.
The user-facing manual lives in the wiki
([Design Tab](https://www.orcaslicer.com/wiki/design_tab)), not here. This document covers the
@@ -118,6 +119,9 @@ referencing the source file, so a project opens without the STEP or mesh it was
The cost is that saved projects are coupled to an OCCT BRep revision.
`tests/data/cad_brep_occt76.brep` holds a solid written by OCCT 7.6, and its test fails if the
bundled OCCT can no longer read it.
A Text feature follows the same rule: it stores the outlines it was vectorised into alongside
its string, font and height, so the project opens identically on a machine that lacks the font;
the three parameters are only what an edit reopens the dialog with.
## The interaction contract
@@ -132,7 +136,7 @@ contract between them is stated in code rather than spread across handlers.
| `Transient` | a value field or a popup menu | closes it; the tool stays armed |
| `Gesture` | an uncommitted delta — an entity being drawn, a body being dragged | reverts it; committed work is untouched |
| `Tool` | a feature card, an armed sketch tool, a constrain session | exits it; drawn entities survive |
| `Idle` | nothing transient | clears the selection; leaves a sketch session only if it is empty |
| `Idle` | nothing transient | clears the selection, a Feature tree or Bodies row included; leaves a sketch session only if it is empty |
`cad_escape_level()` is a `constexpr` free function over a POD of four booleans rather than a
method on the panel, so the ordering that is the entire contract is checkable without a window,
@@ -145,26 +149,126 @@ explicit selection, the sketch ribbon's Cancel, which asks first, or `Ctrl+Z`. A
*session* is deliberately not a `Tool` level; it is the environment the `Idle` level lives in,
which makes the destructive path unrepresentable rather than merely unlikely.
Right-click is read at button-up against two independent budgets — 3 px of drift and 200 ms —
because drift alone still popped a menu at the end of a slow, careful orbit. The raycast uses
the press position, not the release. An armed sketch tool that already consumed the right
Right-click is read at button-up against one budget, 3 px of drift, applied to the whole press
rather than to its end points: a press that wandered past the budget at any moment is
navigation, even if it comes back to where it started, which is what stops a slow, careful
orbit from ending in a menu. There is no time budget — a gesture that means something different
when it is slow is exactly what the interaction charter rules out. The raycast uses the press
position, not the release. An armed sketch tool that already consumed the right
button (to terminate a chain, say) declines to also open a menu, through a read-and-clear flag.
Past either budget the event is navigation, and navigation does not transition the state
machine.
Navigation itself is Prepare's: the camera reads the drag actions set in Preferences > Control
for each button. The left button is shared with picking, so a whole body is swept with a
rectangle on plain left-drag only while no camera action is assigned to it, and with
Shift+left-drag otherwise — Prepare's own rectangle selection.
Entering a sketch changes three things at once so the mode is legible: a banner above the
canvas (a sibling of the canvas, not a child over it — on GTK a child window over a
`wxGLCanvas` is a native window and does not reliably stack over GL), the printer bed muted so
a plate grid is never read as a sketch grid, and `N` to look normal to the plane. Code that
changes any of the three belongs with a change to this section.
## Rendering the bodies
The tab draws its bodies through the same `GLCanvas3D` object path as Prepare, so how they look
is decided in the shared object shader, not in the tab. The slicer's two lights both sit near
the camera, which leaves the sides of a part in nearly one tone; the Design canvas asks for a
studio model instead — a world-space sky/ground hemisphere, a key and a fill light, a
plastic-like highlight and a darker silhouette — through `GLCanvas3D::set_studio_lighting()`
and the phong shader's `lighting_model` uniform. The program is shared by every canvas, so each
use sets the uniform (0 for the slicer's canvases) rather than relying on a default: a canvas
that left it alone would inherit whatever the last canvas chose.
The B-rep edges of every body are drawn over it by the sketch overlay as thin view-facing
ribbons, depth tested and pulled a few pixels toward the eye so they win against the faces that
meet at them and still hide behind faces in front; lines are not used because they do not
rasterise under the software GL context the tab also supports. Seams of closed surfaces and
degenerate edges are left out (`GeometryEngine::display_edges`), and the polylines are sampled
once per shape, keyed by its `TShape`, because a recompute that leaves a body unchanged is the
common case.
While a feature card is open, its preview ghost is the whole model the candidate would produce,
drawn translucent over the bodies, so every face the feature leaves alone is in both at the same
depth. The ghost is drawn with a depth bias that pushes it back (`GLVolume::depth_bias`), so on a shared face
the body always wins instead of the two copies z-fighting, and the ghost shows only where the
result reaches past the bodies. Material a feature removes lies inside the old solid and would not
show at all, so the tools whose result mostly coincides with the body — Fillet/Chamfer, Draft,
Hole and the Mate hover — hide the bodies once the preview is valid and draw the result alone,
opaque.
## Showing what is selected
A selection is drawn on the faces it names, never as a tint over the body: a translucent
selection colour blended into the body's own colour turns a different hue on every body and
vanishes on one close to it. Selected faces are split out of their body into a volume of their
own, which the canvas draws opaque in the selection colour through the same shader and lighting
as the body (`DesignCanvas::rebuild_bodies`); the sketch overlay outlines them with a cased line
— a dark band under a selection-coloured one — so the outline still reads on a body that wears
the selection colour itself. A body picked whole, a face picked in the viewport and the faces of
the Feature tree's selected feature all draw this way. The hover pre-highlight is the outline
alone, uncased: it promises a click, it is not one. The automatic body colours keep clear of the
selection colour's blues and teals, so no body looks selected before anything is picked; a colour
the user sets on a body is theirs, and the cased outline keeps its selection readable.
Selecting a feature row lights the faces that feature made, not the whole body it sits on, so a
fillet row shows its round and the extrude under it keeps the faces the fillet trimmed.
`CadDocument::faces_made_by` answers it without per-feature history: it replays the recipe to
just before the feature and then the feature alone, and a face of the finished model belongs to
the feature when an interior point of it lies on the boundary afterwards and not before, facing
the same way — the facing keeps a block stacked on a base the owner of its bottom face. A feature
that makes no face of its own, such as a Boolean union, answers with the bodies it changed. The
replay costs up to a recompute, so the panel finds the faces once per row and topology
generation, off the UI thread, and only while no feature card is open. One selection is live at
a time: a viewport pick clears the feature row and a feature row clears the viewport pick, as the
Feature tree and Bodies list do between themselves. `Esc`, a click on empty space and an
empty rubber band all let go of it, whichever list or pick made it.
## Following the app
The tab is a page of Orca's main window and answers to the same settings as Prepare.
- **Theme.** Its chrome is coloured from a table of light/dark token pairs. A theme switch
reaches `DesignPanel::on_sys_color_changed` from `MainFrame`, which moves every colour that is
one theme's token onto the other theme's and then runs the app's own dark pass; the icons are
Orca's sidebar grey, which the icon cache maps per theme, so they are re-rasterised rather
than re-tinted.
- **Scale.** Sizes are in DIP, and a DPI change reaches `DesignPanel::msw_rescale`, which
re-rasterises every icon (button faces, flyout rows, card headers, the feature and body lists'
row icons).
- **Sidebar icons.** Every clickable icon in the sidebar shows a hover chip. The card-header and
constraint-row buttons are Orca's self-painted `Button`, because a native button cannot take a
hover background on macOS. The Feature tree and Bodies lists are a custom-drawn
`DesignRowList` rather than a `wxTreeCtrl`, so each row carries its own actions — Edit,
Show/hide and Delete on a feature, Move, Show/hide and Delete on a body — and the eye shows
whether that row is hidden.
- **Viewport text.** The status line and the active tool's values are drawn by the canvas in
its ImGui pass, so they go with the canvas: a top-level window over GL does not follow its
frame and was left floating over other applications.
- **Undo.** The tab keeps its own history (the recipe is not part of Prepare's snapshots), but
it has no Undo/Redo of its own: the top bar, `Ctrl+Z` and Edit drive it while the tab is
shown, greyed to what an undo would actually do.
- **Docking.** The sidebar docks like Prepare's — either side, floating, resized, or collapsed
with the canvas's collapse button or `Shift+Tab` — through its own AUI manager under the
toolbar, because Prepare's manages the Plater and the Plater is not on this page. The button is
the canvas's own toolbar rather than Prepare's, which collapses Prepare's sidebar. The layout,
collapse included, is kept apart from Prepare's (`design_window_layout`) and starts where
Prepare's sidebar is, at its width, so the canvas edge holds still across the tab switch until
the user moves one of them. A floating sidebar is a top-level window, so it is hidden with the
tab rather than left over the other pages, and View > Reset Window Layout resets both tabs.
## The offer is generated, not hand-written
Right-clicking geometry opens the *offer*: eight families in a fixed order, each verb at a
permanent row index, verbs that do not apply shown disabled **in place with their reason**
rather than removed. The invariant is that a verb's row index is identical in every selection
where it appears and that adding a verb never moves an existing one — the hand learns the
position, so the menu is never re-sorted, compacted or adaptively ordered.
position, so the menu is never re-sorted, compacted or adaptively ordered. Above the families
sits one *flat* row, holding Rename and Color — what a selection is opened for most: its verbs are
items of their own at the top of the menu rather than a family's submenu. It is appended after
the eight, so it moved no existing index, and it reads the same from the viewport and from a row
of the Bodies list.
An invariant across 92 verbs and 20 selection kinds does not survive by review, so the map
exists once, as data: `scripts/CAD/tool_atlas.json` carries every verb with its row, key, icon,
@@ -195,6 +299,7 @@ scripted action and a clicked one cannot diverge. It is off unless the variable
| `src/libslic3r/CAD/SketchSolver.*` | constraint solving, over the vendored solver |
| `src/libslic3r/slvs/` | vendored 2D constraint solver (GPLv3) |
| `src/slic3r/GUI/CAD/DesignPanel.*` | the tab: toolbar, feature cards, tree, key maps |
| `src/slic3r/GUI/CAD/DesignRowList.*` | the Feature tree and Bodies lists, with per-row actions |
| `src/slic3r/GUI/CAD/DesignCanvas.*` | viewport integration |
| `src/slic3r/GUI/CAD/DesignSketchTool.*` | in-canvas sketching |
| `src/slic3r/GUI/CAD/DesignInteraction.hpp` | the Esc level contract |
-17
View File
@@ -62,23 +62,6 @@ Clipper2 behaves differently by default, the wrapper adjusts it.
clip do not leave slivers.
- Open polylines are clipped with the non-zero rule and keep their direction.
### Tiled booleans
The sweep slows down with the number of edges crossing a scan line, so a layer
cut into thousands of pieces makes every whole-layer boolean expensive.
`diff_ex_by_piece()` and `intersection_ex_by_piece()` take a subject of
non-overlapping `ExPolygons`, group them into tiles with
`ClipperUtils::tile_expolygons()`, and run each tile in parallel against only
the clip polygons near it, cut to the tile's box. Below 128 pieces there is a
single tile, and they are the plain `diff_ex()` / `intersection_ex()`.
The result covers the same area as the plain call. Without the safety offset
the rings are the same. With it, each tile unites only the clip polygons near
it, so a clip edge that the whole-layer union splits where it crosses a distant
clip polygon stays whole, and a crossing with the subject can round 1 unit
differently. The tiles' results are concatenated in tile order, so the order of
the output `ExPolygons` differs from the plain call.
### Offsets
- Before offsetting, input vertices closer than
+529 -183
View File
@@ -2,238 +2,584 @@
## Purpose and scope
Precise Seam places the seam where a helper volume intersects the external
wall. The user attaches a mesh to an object as a Precise Seam modifier, and on
every layer the seam placer reads the modifier's slice to decide where the seam
of each external perimeter may, must or must not go. The same mesh keeps
working after the model changes, so the seam does not have to be repainted
after every design revision, and a swept helper body can guide the seam along
any path.
Precise Seam lets a helper volume decide where the seam of an object goes. The
user attaches a mesh to an object as a Precise Seam modifier. On every layer,
the part of the external perimeter that lies inside the modifier's slice
determines where the seam must, may or must not be placed. The helper is a
persistent model object rather than paint on the surface, so it keeps working
when the design changes. A body swept along a path on the surface can guide the
seam along any trajectory.
The modifier is non-printing geometry. It does not take part in slicing, region
assignment, filament selection or brim adhesion. It affects only seam
placement, which runs during G-code export.
The modifier is non-printing geometry. It takes no part in object slicing,
region assignment, filament selection or brim adhesion, and it affects only seam
placement during G-code export. Objects without Precise Seam volumes follow the
regular seam placement unchanged.
## Volume types and priority
Precise Seam does not replace the seam placer. It feeds it: a modifier inserts
the points it needs into the perimeter and changes the enforced/blocked type of
seam candidates, the same typing mechanism as seam painting, and the configured
seam position then chooses among them.
Precise Seam adds six `ModelVolumeType` values after `SUPPORT_ENFORCER`. The
strong types come first and the weak types follow. `is_precise_seam()`,
## Modifier types
Precise Seam adds six `ModelVolumeType` values after `SUPPORT_ENFORCER`, strong
types first and weak types after them. `is_precise_seam()`,
`is_precise_seam_strong()` and `is_precise_seam_weak()` are range checks that
depend on this order.
| Type | Group | Effect on the perimeter |
| Type | Group | Effect on an intersected perimeter |
| --- | --- | --- |
| `PRECISE_SEAM_CENTER` | strong | seam at the arc-length midpoint of the intersection |
| `PRECISE_SEAM_CENTER` | strong | seam at the midpoint, by arc length, of the intersection |
| `PRECISE_SEAM_LEFT` | strong | seam at the first point of the intersection |
| `PRECISE_SEAM_RIGHT` | strong | seam at the last point of the intersection |
| `PRECISE_SEAM_ENFORCED` | weak | intersection marked as enforced |
| `PRECISE_SEAM_BLOCKED` | weak | intersection marked as blocked |
| `PRECISE_SEAM_NEUTRAL` | weak | intersection reset to neutral |
A strong modifier fixes one point. A weak modifier only changes the
enforced/blocked type of seam candidates, and the configured seam position then
chooses among them. First and last are taken along the perimeter made
counter-clockwise seen from above. On an outer wall seen from outside, Left is
the left end of the intersection. On the wall of a hole seen from inside the
hole, the two ends are swapped.
A strong modifier fixes a single point. The perimeter gets exactly one enforced
seam candidate there, and every other candidate is blocked. A weak modifier
retypes, and where needed adds, the candidates inside its intersection, like
painting does.
An **intersection** is a continuous part of the external perimeter's centerline
that lies inside the modifier's slice on that layer. It is a portion of the
perimeter, never a chord through the object. The centerline lies half an
extrusion width inside the model surface and depends on print settings, so a
modifier must reach clearly past the surface to cross it unambiguously.
### Terms
- **Segment:** an intersection as the code represents it (`PerimeterSegment`).
User-facing texts call it an intersection.
- **Fragment:** a piece of the perimeter returned by clipping, before it is tied
to the source contour.
- **Interval:** the bound part of one source edge, given by the edge index and a
parameter range on that edge.
- **Zone:** a weak segment with its type (Enforced, Blocked or Neutral).
- **Boundary:** an end of a zone, inserted into the perimeter polygon.
- **Candidate:** a seam candidate of the seam placer, built from the points of
the processed perimeter polygon (painted enforcers may add more).
First and last are taken along the perimeter oriented counter-clockwise as seen
from above. On an outer wall seen from outside, Left is therefore the left end
of the intersection. On the wall of a hole seen from inside the hole, the two
ends are swapped. Mirroring an object does not mirror the mode: perimeters stay
counter-clockwise, so Left remains the left end seen from outside, and the seam
moves to the other end of the modifier instead of following the mirrored model.
## Priority
The order of volumes in the object is the priority order, highest first.
`ModelObject::sort_volumes()` keeps every strong modifier before every weak one
and preserves the user's order within each group. The object list lets the user
drag a modifier only within its own group. A type change that crosses a group
boundary moves the volume to the end of its new group, where it has the lowest
priority. Strong modifiers are tried in this order, and the first one that
yields a seam on a perimeter wins. Weak modifiers are applied from the lowest
priority to the highest, so the highest one overwrites any overlapping zone.
drag a modifier only within its own group. A type change that crosses the group
boundary moves the volume to the end of its new group, with the lowest priority
there.
## Model storage and 3MF compatibility
- **Strong:** modifiers are tried in priority order on each perimeter. The first
one that yields a usable segment decides the seam. Within that modifier the
longest segment wins; lengths are never compared across modifiers. Once a
strong point is placed, no later strong modifier and no weak modifier is
processed for that perimeter.
- **Weak:** every weak modifier applies. They are applied from the lowest
priority to the highest, so the highest one overwrites overlapping zones. A
Blocked modifier that fully contains a perimeter is the exception: it is
skipped there (see [Full containment](#full-containment)).
Projects must stay readable by earlier releases, and the modifier must not
change a print there. Both 3MF writers therefore store a Precise Seam volume as
an ordinary parameter modifier: `modifier_part` in the Bambu-format part
subtype, and `ParameterModifier` together with the legacy `modifier` flag in
the Prusa-format volume metadata. The seam mode is written separately under
A strong modifier without a usable segment, even one whose fragments were all
discarded, passes the turn to the next one.
## Data flow
1. **Invalidation.** `Print::apply()` treats a change of Precise Seam volumes as
a change of seam placement and invalidates G-code export; the object is not
resliced (see [Print invalidation](#print-invalidation)).
2. **Modifier slices.** `SeamPlacer::init()` collects each object's Precise Seam
volumes once, slices every volume separately and caches its regions with
their bounding boxes.
3. **Perimeters.** Seam candidates are gathered in parallel over the layers.
For objects with Precise Seam volumes, each external perimeter polygon is
normalized and prepared once for all modifiers.
4. **Extraction.** For each modifier, the perimeter is clipped against the
modifier's regions on that layer. The clipped fragments are bound back to the
source edges of the perimeter and assembled into segments.
5. **Strong, then weak.** Strong modifiers try to insert one seam point into the
perimeter polygon. If none succeeds, weak modifiers insert their zone
boundaries and subdivide enforced edges.
6. **Candidates.** The seam placer builds candidates from the modified polygon.
Painting assigns types first, weak zones overwrite them, and a strong point
makes its candidate the only enforced one.
7. **Selection and restoration.** The configured seam position chooses the
seams and aligns them. Afterwards the exact strong points are restored.
8. **Warnings.** After all objects are processed, `SeamPlacer::init()` prepares
one combined warning text if any problem was found; G-code export issues it.
## Modifier slices
`init_precise_seam_data()` collects the Precise Seam volumes of each object:
strong ones in priority order and weak ones in reverse, so that weak zones can
be applied with last-write-wins. Each volume is sliced separately with
`PrintObject::slice_single_volume_regions()`, at the object's layer heights and
with the same centered transformation as the object. The slices keep every
region's outer contour together with its holes as an `ExPolygon`. Volumes are
not merged, so each keeps its own priority, and a modifier may have several
regions on one layer.
`prepare_modifier_slices()` moves the slices into `ModifierRegionsCache`,
pairing each region with the bounding box of its exterior. Empty layers keep
their slots, so the cache is indexed by object layer; `Layer::id()` includes raft
layers, which are subtracted. The cache is filled before candidates are gathered
and is only read afterwards, shared by both modifier kinds and all worker
threads without locking.
## Perimeter preparation
The seam placer works on external perimeter loops, including the walls of
holes. For objects with Precise Seam volumes, consecutive duplicate points and
the repeated closing point of each extrusion loop are removed: adjacent
extrusion paths share endpoints, and the resulting zero-length edges would
prevent point insertion at their junctions. Distinct visits to one point of a
self-touching contour are kept. Objects without Precise Seam volumes keep their
original points, so ordinary seam candidates are unaffected.
Each polygon is made counter-clockwise. A single `PreparedPerimeter` is then
built for all modifiers of that perimeter. It holds a validity check (at least
three points, no consecutive or closing duplicates), the bounding box, and the
clipping line: the polygon as an open polyline with its first point repeated at
the end. The preparation borrows the polygon and is used only while the polygon
is unchanged: strong processing returns immediately after inserting its point,
and weak processing collects all segments before it inserts anything. An
invalid perimeter receives no Precise Seam processing.
## Segment extraction
`extract_perimeter_segments()` turns one modifier's regions on one layer into
segments of the perimeter, each with its geometry and its position on the
source contour. Both modifier kinds consume these segments; the extractor is
told the modifier type so that it prepares only the data that type needs.
### Clipping
Regions whose bounding box does not overlap the perimeter's are skipped. The
clipping line is intersected with each remaining region by `intersection_pl()`,
which clips an open path against an `ExPolygon` with its holes attached, using
the nonzero rule. Clipping an open line yields only pieces of the perimeter, so
a modifier crossing the whole object produces two separate pieces rather than a
chord through the body. Holes in a modifier and several regions of one modifier
simply produce more pieces. The line is cut at vertex zero, so a piece crossing
that vertex arrives as two fragments. A border that only touches the line can
come back as a single point; such fragments carry no coverage and are dropped
before binding.
### Binding fragments to source edges
Clipper returns coordinates only. Insertion needs the source edge of every
point, and coordinates alone are ambiguous where a contour visits the same
point twice. Each fragment is therefore bound to the source edges it covers,
producing intervals: an edge index with a parameter range on that edge.
- **Exact path.** For fragments with interior points, the second point is used
as an anchor that must equal a source vertex exactly. Clipping keeps the
vertices of an open path unchanged, including collinear ones. The following
points must match successive source vertices in either direction; later
occurrences of the anchor are tried if a sequence does not match. Only the two
end cuts are projected onto their edges.
- **Projection path.** Two-point fragments, and fragments the exact path cannot
match, are bound by projection. The first source edge that holds both points
of the first pair, with distinct parameters, establishes the edge and
direction. Every following pair must continue on the same edge or cross to the
neighboring edge at their actual shared vertex, in the same direction. A pair
continuing on the same edge reuses the previous pair's parameter for their
shared point, so the two projections of one point cannot differ.
- **Failure.** A fragment that cannot be bound continuously is rolled back and
discarded. Earlier fragments and other fragments are unaffected. The failure
is counted, logged and reported to the user (see
[Diagnostics](#diagnostics-and-warnings)).
Two rare rounding cases are handled only after both paths have failed, so the
normal path never pays for them:
- **Cut beside a vertex.** When a modifier boundary crosses within about one
coordinate unit of a source vertex, Clipper can place the cut at the vertex's
height but a few units beside it. The end pair then collapses to the vertex's
parameter or misses both neighboring edges. An end cut closer than the
snapping radius to a vertex of the fragment's own chain is snapped to that
vertex: either its neighbor in the fragment (the cut is a rounded copy of it
and is dropped) or a vertex that shares a source edge with that neighbor. The
neighbor wins whenever it is within the radius. Ends that are themselves source
vertices and ambiguous choices are left unchanged. Binding is then retried
once with the same strict rules, so a wrong candidate can only fail again.
- **Contact.** A fragment that still fails but is shorter than the snapping
radius is accepted as a contact and binds nothing. Insertion would collapse it
onto one point anyway.
Both outcomes are recoveries, not failures: they show no user warning but leave
a log marker.
### Assembling segments
The intervals are sorted by edge and parameter. Intervals on the same occurrence
of an edge are united when they overlap or meet, by parameter or at the same
integer point; equal coordinates on different edges are never united. A
parameter of 1 is stored as parameter 0 of the next edge, so intervals on
adjacent edges meet exactly at their shared vertex. Consecutive intervals that
meet form one `PerimeterSegment`, and the last segment is joined with the first
when they meet at vertex zero, undoing the artificial cut of the clipping line.
Each segment keeps its polyline, the source edge of every polyline edge, and its
begin and end positions on the source contour.
### Full containment
A modifier that covers the whole perimeter has no boundaries on it. The policy
follows seam painting, where painting a whole perimeter green is a meaningful
choice and forbidding the seam all round is not:
- **Seam Enforced** types the whole perimeter, like a perimeter painted green all
round, with subdivision applied as described under [Weak modifiers](#weak-modifiers).
- **Seam Neutral** types the whole perimeter Neutral, like an unmarked perimeter,
clearing painting and lower zones.
- **Seam Blocked** is skipped for the perimeter, with the full-containment
warning. The seam cannot avoid the whole perimeter, so the modifier does not
override anything below it: lower zones and painting stay in effect.
- **Seam Center, Left and Right** are skipped with the same warning: there is no
intersection to place the point on.
Enforced and Neutral take part in the usual priority order (see
[Weak modifiers](#weak-modifiers)).
The perimeter is fully contained when the united intervals cover every source
edge from parameter 0 to 1. A modifier boundary that merely touches the
perimeter counts as well:
- At a vertex or on an axis-aligned edge, clipping splits the line exactly at the
touch, the pieces meet at one point, and the coverage is complete.
- On an inclined edge the touching point is usually not representable on the
integer grid. The boundary pokes a few units across and leaves a real gap, so
a single segment covers everything except that gap.
Weak insertion would collapse such a segment's boundaries onto one vertex and
turn the intended zone into a single candidate, and strong would put the seam at
the touch. A single segment is therefore also full containment in the cases
where insertion collapses it, exactly up to edges shorter than 2 µm:
- the uncovered length from its end to its begin is below 1 µm, or
- the gap spans one vertex, or starts at a vertex and ends on the next edge, and
both ends lie within 1 µm of the vertex that ends the first gap edge, since
each end then snaps onto it from its own edge.
A cheap filter runs first: both cases bring the segment's ends within 2 µm of
each other.
## Strong modifiers
For a strong modifier, the extractor prepares each segment's target point
before anything is inserted, together with the source edge it lies on:
- **Left:** the segment's first point.
- **Right:** the segment's last point.
- **Center:** the point at half the segment's arc length.
Arc length is the sum of Euclidean edge lengths, not the chord or a vertex count.
`insert_strong_seam_point()` selects the longest segment of the first modifier
that has one. Exactly equal lengths are resolved by the prepared target points:
greater bed Y first, then smaller X; a complete tie keeps the first segment.
Slice coordinates already include instance rotation and have the bed axes;
centering and XY translation do not change this order. Nearly equal lengths are
not treated as equal, so exact ties occur mainly on axis-aligned geometry.
Geometrically equal segments, such as a symmetric modifier crossing both faces
of a thin wall, differ only by rounding noise that varies between layers, so
the chosen face may alternate. This is accepted deliberately: such a modifier is
ambiguous by itself: more than one segment raises the "multiple intersections"
warning. The user should make the modifier cross the perimeter once.
The selected point is inserted on its source edge. A point within 1 µm of an
existing vertex is snapped to that vertex. Helper points are added 1 µm on both
sides of it, except on an adjacent edge shorter than 2 µm, which already bounds
the distance.
When the candidates are built, the candidate at the inserted point is the only
enforced one and becomes the central enforcer; every other candidate is blocked.
Every seam position mode therefore selects it. Alignment and random placement
can still move the final position, so after alignment
`restore_precise_seam_positions()` writes the exact point and its index back
into every perimeter that has a strong seam.
## Weak modifiers
`collect_weak_modifier_segments()` extracts the segments of every weak modifier
before the polygon is modified, so all positions refer to the same contour. Each
segment becomes a zone with a type and two boundaries, kept in application
order, lowest priority first. Full containment of an Enforced or Neutral
modifier becomes a whole-perimeter zone at its place in that order: it has no
boundaries and takes part in no insertion or helper step below. The boundaries
carry their positions on the source contour; these remain as provenance after
insertion and are not indices into the modified polygon.
`prepare_weak_modifier_segments()` then changes the polygon:
1. **Boundary insertion.** Insertion events are sorted by decreasing source edge
and parameter, and the polygon is modified from its end towards its start. A
pending boundary's source index therefore stays valid. Vertex zero has the
canonical position `(0, 0)` and is
processed last, and a point on the closing edge is appended rather than
inserted at index zero. A boundary within 1 µm of either endpoint of its
current edge, an original vertex or a boundary inserted earlier, is snapped to
that point, so coincident boundaries share a vertex. A zone narrower than
1 µm collapses into a single vertex.
2. **Helper points.** A helper point is added 1 µm outside every boundary,
unless the edge there is shorter than 2 µm, which already bounds it. The
helpers keep the edges at a boundary short, so a seam placed along such an
edge stays close to the boundary. Coincident boundaries share their helpers.
3. **Enforced subdivision.** Zone types are resolved for the polygon's edges in
priority order. The edges of a zone are those from its left boundary up to,
but not including, its right boundary; a whole-perimeter zone types every
edge. Enforced edges longer than `SeamPlacer::enforcer_oversampling_distance`
(0.2 mm) are subdivided into steps of at most that length; shorter edges and
existing vertices are kept.
The regular seam placer then chooses the seam as for painted seams.
When candidates are built, painting assigns their types first.
`apply_weak_modifiers_to_perimeter()` then overwrites the types of the
candidates between the boundaries of each zone, both boundaries included,
lowest priority first; a whole-perimeter zone types every candidate. Blocked
and Enforced zones therefore take precedence over painting, and Neutral clears
painting inside its zone.
## Numeric tolerances
Coordinates are integers in scaled units: 1 nm by default, and 10 nm when a bed
larger than 2147 mm switches `SCALING_FACTOR`. Both Precise Seam tolerances are
deliberately defined in units rather than physical distances. Clipper truncates
cuts to whole units at any scale, so the on-edge tolerance must follow the unit; the
snapping radius scales with it to keep its margin over single-precision
candidate coordinates, which are coarser on large beds. Distances quoted in
this document in nanometers and
micrometers assume the default unit; on large printers they are ten times
larger. The enforced subdivision step is a physical distance and stays 0.2 mm.
| Value | Role |
| --- | --- |
| `MACHINE_PRECISION_SQUARED` (2.5 units², about 1.6 nm) | A point lies on an edge if it is this close. It absorbs Clipper's truncation of cuts to whole units (under √2 units from the edge) and never bridges a real gap: a one-unit uncovered gap stays a gap. |
| `TOLERANCE_LINEAR` (1000 units, 1 µm) | Insertion snaps points this close to an existing vertex, and helper points are placed this far from boundaries. The same radius bounds the rounding fallback, contacts and the sub-micron full-containment rule, so those decisions match what insertion would produce anyway. |
| `enforcer_oversampling_distance` (0.2 mm) | Maximum step of enforced subdivision. |
Raising the on-edge tolerance would not help with cuts beside a vertex: more
points past a vertex would be clamped to its parameter and collapse. Lowering it
would reject ordinary rounded cuts. The snapping radius is kept far above
clipping precision for robustness: seam candidates hold single-precision
coordinates, whose step is about 8 to 15 nm at typical object coordinates
(about 0.25 µm 3 m from the object's centre, on large beds only), and
weak boundaries and the strong point are located among the candidates by those
coordinates, so distinct points must stay clearly distinct. 1 µm is also far
below printing precision.
## Diagnostics and warnings
One `PreciseSeamWarnings` instance is shared by all objects and layers of a
`SeamPlacer::init()` call. After all objects are processed, `SeamPlacer::init()`
prepares at most one warning text, available through `precise_seam_warning()`.
G-code export issues it as one non-critical warning with the ID
`SlicingPreciseSeamWarning`. It is a single line, "Precise Seam: <causes>. Seam
placement may differ from expected.", because the export warnings dialog shows
only the first line of each warning. Repeated warning events replace the
notification instead of appending to it. Except for the "had no effect" cause,
the causes name the modifier types involved, as the menu names them, in menu
order and each type once, for example "(Seam Left, Seam Enforced)".
The causes are:
- **failed to process some intersections (types):** at least one fragment was
discarded by binding. Other segments remain usable.
- **multiple intersections with a perimeter, only one was used (types):**
a Seam Center, Left or Right modifier had more than one segment on a
perimeter (see [Strong modifiers](#strong-modifiers)).
- **a perimeter is fully inside a modifier, the modifier was not applied to it
(types):** a Seam Center, Left, Right or Blocked modifier was skipped for a
perimeter (see [Full containment](#full-containment)).
- **modifier "<name>" of "<object>" had no effect on the seam (it might not reach
the centerline of the printed perimeter):** a modifier was evaluated on at
least one perimeter and never gave a segment, full containment or a discarded
fragment. Only the first such modifier in print and volume order is named,
followed by "(N in total)" when there are several.
Only the effect is certain, so the cause is given as a hint. A modifier is
evaluated only when its turn comes: on a perimeter where a higher strong
modifier placed the seam, lower strong and all weak modifiers are not
evaluated. A modifier that was never evaluated is not reported, since nothing
is known about it. A point contact gives no segment and does not count as
reaching the perimeter.
The log records the following diagnostic markers:
- `[PreciseSeamIntersectionFailed]` for a discarded fragment, with object,
modifier, layer, height, fragment and failing pair, the failure reason and
point counts.
- `[PreciseSeamFragmentRecovered]` for a recovery, with `outcome=bound` or
`outcome=contact`, the same location fields and the original failure reason.
- `[PreciseSeamNoEffect]` for every modifier of the "had no effect" cause, with
the object and modifier names. Unlike the user warning, the log lists all of
them.
Failures and recoveries are counted separately. The first 10 of each per
`init()` call are logged in detail, in parallel processing order; if a limit is
exceeded, one summary marker reports the total and the number omitted.
## Known limitations
- **The modifier must reach the perimeter centerline.** Contacts are taken as
clipping returns them, without offsets or tangency rules, so boundaries that
only graze the centerline are the user's responsibility. Several near-touches
on inclined edges can leave several segments separated by gaps of a few units;
their zones then cover nearly the whole perimeter instead of being treated as
full containment.
- **Self-touching perimeters.** Extraction keeps distinct visits of one
coordinate apart through its source-edge bindings, but the consumers locate
inserted points by coordinates. A weak zone is typed and subdivided from the
first vertex with its boundary coordinate, while boundary helpers are added at
every such vertex. A strong point marks every candidate at its coordinate as
enforced, and the last one is restored after alignment. If a boundary or a
strong point falls exactly on a repeated coordinate, a zone may therefore start
from another visit, or the seam may start at another visit of the same point.
Carrying visit identity through insertion, refinement, candidates and
restoration would touch the whole pipeline, so it is not done for this rare
geometry. Overlapping source visits are likewise outside the binding contract.
## Integration with the application
### Other seam settings
- Precise Seam takes part only in outer and hole perimeter seam placement. In
spiral vase mode the seam placer is not used for perimeters, so the modifiers
have no effect.
- Scarf seams, the seam gap and wiping start from the chosen point exactly as
they would from an ordinary seam.
- Seam painting acts only from model parts, the volumes the seam gizmo shows and
edits, and from negative volumes. Painting retained on a volume after a change
from part to a Precise Seam, ordinary or support modifier is ignored. A type
change back to a model part reactivates any retained painting.
Negative volumes keep it on purpose: painting a
part and turning it into a negative volume is the only way to paint the wall
of the hole it cuts. That painting still affects the seam but is invisible in
the gizmo and cannot be edited there; this is known technical debt.
If painting them is ever made editable, G-code invalidation must track it too:
`model_custom_seam_data_changed()` checks model parts only.
### Model storage and 3MF compatibility
Projects must stay readable by earlier releases, and a Precise Seam volume must
not change a print there. Both 3MF writers therefore store it as an ordinary
parameter modifier: `modifier_part` in the Bambu-format part subtype, and
`ParameterModifier` together with the legacy `modifier` flag in the
Prusa-format volume metadata. The seam mode is written separately under
`precise_seam_type`, using the names from `ModelVolume::type_to_string()`
(`precise_seam_center` and so on).
On load, the mode applies after all other volume metadata, regardless of XML
key order, and only when the base type is a modifier. Missing or unknown modes
leave an ordinary modifier. Seam metadata on any other base type is ignored.
Files that stored the seam mode directly as the volume type still load.
On load, the mode is applied after all other volume metadata, regardless of XML
key order, and only when the base type is a modifier. A missing or unknown mode
leaves an ordinary modifier, and seam metadata on any other base type is
ignored. Files that stored the seam mode directly as the volume type still load.
A project saved again by an earlier release loses the seam mode for good: the
volumes stay ordinary modifiers without settings.
A Precise Seam volume keeps any per-volume settings it had as a part or
modifier, but they are inactive and the object list shows no settings item for
it. The writers prefix these keys with `precise_seam_config:`, so an earlier
reader drops them as unknown options. The volume therefore loads there as a
modifier without settings and has no effect on the print. The current reader
restores the keys only when the volume ends up as a Precise Seam type, so the
settings return when the user changes the type back. Configuration values are
XML-escaped in both writers, for every volume type.
reader drops them as unknown options and loads a modifier without settings,
which has no effect on the print. The current reader restores the keys only when
the volume ends up as a Precise Seam type, so the settings return when the user
changes the type back.
## Print invalidation
### Print invalidation
`Print::apply()` compares the Precise Seam volumes of each object by type, ID
and transformation. Adding, removing, moving, reordering or retyping one
cancels background processing and invalidates only `psGCodeExport`; the sliced
layers are kept. `model_volume_list_update_supports_and_seams()` then brings
the support and Precise Seam volumes of the print's model copy in line with the
new model in one pass. A volume may switch between the two families, since
neither affects slicing. A conversion to or from a part or ordinary modifier
changes the solid and modifier volume lists and reslices as before.
and transformation. Adding, removing, moving, reordering or retyping one cancels
background processing and invalidates only `psGCodeExport`; the sliced layers
are kept. `model_volume_list_update_supports_and_seams()` then brings the
support and Precise Seam volumes of the print's model copy in line with the new
model in one pass. A volume may switch between these two families, since neither
affects object slicing; such a switch also changes the support volumes, so the
support step is invalidated as well.
## Modifier slices
A conversion to or from a part or an ordinary modifier changes the solid and
modifier volume lists and reslices the object as before. The volume keeps its
ID across the type change, so the region cache treats a former support or
Precise Seam volume that became a part or modifier as new, since it was never
cached.
`SeamPlacer::init()` collects the Precise Seam volumes of each object once:
strong ones in priority order and weak ones reversed. It slices each volume
separately with `PrintObject::slice_single_volume()`, which shares
`slice_modifier_volumes()` with support blockers and enforcers but does not
merge volumes, so each keeps its own priority. The result is cached per volume
and indexed by object layer; `Layer::id()` includes raft layers, which are
subtracted. Seam candidates are then gathered in parallel over the layers and
read the cache without locking.
Removing the last helper of a single-part object reslices it, as removing any
last modifier would.
Objects without Precise Seam volumes follow the unchanged seam placement path.
For objects that have them, perimeter extraction also removes consecutive
duplicate points and the repeated closing point of each extrusion loop.
Zero-length edges at path junctions would otherwise prevent point insertion
there. Distinct visits to one point of a self-touching contour are kept.
## Finding the wall segment
The seam placer works on the external perimeter loops of each layer, both
outer contours and holes, each made counter-clockwise. For every modifier
polygon on the layer that overlaps the perimeter's bounding box, the region
enclosed by the perimeter is clipped against the modifier polygon. The boundary
of each intersection polygon alternates between runs that follow the perimeter
and runs that follow the modifier outline. The wall segment is the longest
continuous run of intersection vertices that lie on the perimeter, measured in
vertices.
The fast path first finds an intersection vertex that exactly matches a
perimeter vertex. It then walks forward and backward, expecting the adjacent
perimeter vertex and falling back to projection when Clipper has merged or
split collinear edges. A vertex counts as on the perimeter when its projection
is within about 1.6 nm, which covers Clipper's rounding. If no vertex matches
exactly, or every vertex lies on the perimeter, the general path projects all
vertices. When every vertex is on the perimeter, the edge midpoints are checked
instead: a modifier chord can join two perimeter vertices directly, and the
chords split the vertex ring into runs. If no edge leaves the perimeter, the
perimeter lies entirely inside the modifier.
`Polygon::point_projection()` optionally reports the edge that holds the
projection, and every point of the segment keeps the index of its perimeter
edge. New points are inserted on that edge. A point within 1 µm of an existing
vertex snaps to that vertex instead.
## Strong modifiers
For a strong modifier, the target is the first point, the last point or the
arc-length midpoint of the segment. The midpoint is projected back onto the
original perimeter, because Clipper may have merged several perimeter edges
into one segment edge. The target is inserted into the perimeter, and a helper
point is inserted 1 µm before and after it. Strong modifiers are tried in
priority order, the first valid intersection decides the seam, and weak
modifiers are not processed for that perimeter.
When candidates are built, the inserted point is the only enforced candidate
and becomes the central enforcer; every other candidate is blocked. The seam
position modes then pick that point: Aligned and Aligned Back prefer the central
enforcer, while Back, Random and Nearest rank enforced candidates above blocked
ones. Alignment and random placement can still move the final position along an
edge. After alignment, `restore_precise_seam_positions()` writes the exact point
and its index back into every perimeter that has a strong seam. Inner walls take
their seam from the external seam as usual, including staggering.
## Weak modifiers
Weak modifiers produce one segment per intersection polygon, so one modifier can
mark several zones on one perimeter. All segment boundaries are inserted into
the perimeter in order of decreasing arc length. Each insertion then leaves the
indices of the pending, shorter ones unchanged; a point on the closing edge is
appended rather than inserted at index zero. A helper point is added 1 µm
outside each boundary. Random placement picks a position along the edge that
follows a candidate. These helpers keep that edge 1 µm long at each boundary, so
a zone cannot extend or intrude further than that. Boundaries that coincide
share their helper points.
The zone types are then resolved in priority order, and the edges of enforced
zones are subdivided into steps of at most
`SeamPlacer::enforcer_oversampling_distance` (0.2 mm). The middle candidate of
the longest enforced patch is therefore close to the geometric middle of the
zone. That patch is measured in candidates, across the closing edge, regardless
of where the contour starts; the same rule applies to painted seams.
Candidates first receive their type from seam painting. The weak zones then
overwrite it, lowest priority first. Blocked and Enforced zones therefore take
precedence over painting, and Neutral clears painting inside its zone.
## Unsupported geometry and warnings
Some modifier shapes cannot be resolved to one seam or one zone per crossing.
They are detected cheaply and reported rather than guessed:
- A strong modifier that crosses a perimeter in more than one place uses only
its first valid segment. The other crossings are ignored.
- A modifier that crosses the whole region enclosed by the perimeter is
detected when the modifier outline minus that region leaves more than one
piece, none of them a hole. Its intersection holds two wall runs, and only
one of them is used.
- A modifier whose slice has a hole on a layer, found as a clockwise polygon in
the flattened slice, is skipped on that layer. The flattened slice no longer
records which hole belongs to which contour.
- A perimeter that lies entirely inside a modifier is ignored by that modifier.
The conditions are atomic flags shared by all layers and objects. After all
objects are processed, `SeamPlacer::init()` issues at most one non-critical
warning with the ID `SlicingPreciseSeamWarning`. The warning is a single line
that lists every cause found, because the export warnings dialog shows only the
first line of each warning. Repeated warning events replace this notification
instead of appending text to it.
## User interface
### User interface
- *Add Precise Seam* in the object menu creates a Center modifier from a
primitive or a loaded mesh. Text and SVG volumes cannot become Precise Seam
modifiers: the menu does not offer them, and `ObjectList::set_volume_type()`
modifiers: the menu does not offer it, and `ObjectList::set_volume_type()`
refuses the change.
- *Change Type* has a single *Precise Seam* entry. It converts other volumes to
Center and keeps the mode of volumes that are already Precise Seam. The
*Precise Seam Type* submenu appears only when every selected item is a
Precise Seam volume, including settings rows that resolve to one. It sets the
chosen mode on all selected volumes.
*Precise Seam Type* submenu appears only when every selected item is a Precise
Seam volume, including settings rows that resolve to one, and sets the chosen
mode on all of them.
- Each mode has its own icon in the object list and its own color in the 3D
view, at 60% opacity: warm oranges for the strong modes, and green, red and
gray for Enforced, Blocked and Neutral.
- Object list drops map visible rows to volume indices while skipping hidden
cut connectors, and they refresh the row-to-volume map of the object.
- Precise Seam volumes have no filament, block pasting into SLA, and are exposed
to Python plugins as `ModelVolumeType` values plus the `is_precise_seam*()`
methods.
view, at 60% opacity: warm orange, gold and dark orange for Center, Left and
Right; green, red and gray for Enforced, Blocked and Neutral. The three strong
colors are close shades of one orange because all three mark strong
modifiers; the object list icons tell the modes apart.
- Precise Seam volumes have no filament and cannot be pasted into SLA objects.
Python plugins see them as `ModelVolumeType` values and through the
`is_precise_seam*()` methods.
## Implementation and verification
- [PreciseSeam.cpp](../../src/libslic3r/GCode/PreciseSeam.cpp) implements segment
detection, point insertion, weak-zone resolution and position restoration.
[SeamPlacer.cpp](../../src/libslic3r/GCode/SeamPlacer.cpp) integrates it into
candidate gathering and issues the warning.
- [PreciseSeam.cpp](../../src/libslic3r/GCode/PreciseSeam.cpp) implements the
modifier cache, perimeter preparation, segment extraction and binding, strong
selection and insertion, weak-zone preparation and application, and position
restoration. [PreciseSeam.hpp](../../src/libslic3r/GCode/PreciseSeam.hpp)
declares the contracts; [PreciseSeamInternal.hpp](../../src/libslic3r/GCode/PreciseSeamInternal.hpp)
exposes the binding internals to tests.
- [SeamPlacer.cpp](../../src/libslic3r/GCode/SeamPlacer.cpp) fills the cache,
normalizes perimeters, calls both consumers while gathering candidates,
restores strong positions after alignment and prepares the warning text, which
[GCode.cpp](../../src/libslic3r/GCode.cpp) issues during G-code export.
- [Model.hpp](../../src/libslic3r/Model.hpp) defines the types and their order,
[PrintApply.cpp](../../src/libslic3r/PrintApply.cpp) handles invalidation, and
[PrintObjectSlice.cpp](../../src/libslic3r/PrintObjectSlice.cpp) slices the
modifiers. [bbs_3mf.cpp](../../src/libslic3r/Format/bbs_3mf.cpp) and
[3mf.cpp](../../src/libslic3r/Format/3mf.cpp) store them.
[PrintObjectSlice.cpp](../../src/libslic3r/PrintObjectSlice.cpp) slices single
volumes into structured regions. [bbs_3mf.cpp](../../src/libslic3r/Format/bbs_3mf.cpp)
and [3mf.cpp](../../src/libslic3r/Format/3mf.cpp) store them.
- [GUI_Factories.cpp](../../src/slic3r/GUI/GUI_Factories.cpp) and
[GUI_ObjectList.cpp](../../src/slic3r/GUI/GUI_ObjectList.cpp) provide the menus,
type changes and ordering.
type changes and ordering; [3DScene.cpp](../../src/slic3r/GUI/3DScene.cpp)
defines the colors.
- [Segment extraction tests](../../tests/libslic3r/test_precise_seam.cpp) cover
clipping and binding: holes and components, contour origin and reversal,
repeated coordinates, collinear vertices and rounding, rollback and the
diagnostic limits, the rounding fallback on synthetic and real Clipper
fragments, contacts, and full containment including touches and sub-micron
gaps on inclined edges and around vertices.
- [Precise Seam tests](../../tests/fff_print/test_precise_seam.cpp) cover the
strong positions, including a midpoint on an existing vertex or the closing
edge. They also cover shared and coincident weak boundaries, every warning,
and the priority order.
consumers: strong targets in every mode, including a midpoint on an existing
vertex or the closing edge, longest-arc selection and tie order in bed axes,
priorities, weak boundaries that coincide or share an edge, enforced
subdivision, whole-perimeter weak zones with painting and priorities, weak
zones over painting's oversampled candidates, the warning type masks, usage
tracking for the "had no effect" warning, volume sorting of strong and weak
groups, restoration of strong points after alignment, raft layer indexing and
structured slices. End-to-end tests slice a real object with Precise Seam
volumes and check the outer wall starts in the exported G-code: every strong
mode under several seam positions and with a raft, Enforced and Blocked zones,
a modifier with a hole, and the user warning.
- [Seam placer tests](../../tests/fff_print/test_seam_placer.cpp) cover
enforced-patch selection independent of the contour start, fully painted
contours, duplicate removal, and `Print::apply()` synchronization through
type changes and restored model snapshots.
contours, duplicate removal, and `Print::apply()` synchronization through type
changes and restored model snapshots. The duplicate-removal test also checks
the "had no effect" warning text prepared by `init()` for a helper that never
reaches the loop. Further tests check that adding, moving, retyping or
removing a Precise Seam volume invalidates only G-code export, and that seam
painting acts only from model parts and negative volumes, including after a
type change back to part.
- [3MF tests](../../tests/libslic3r/test_precise_seam_3mf.cpp) cover the round
trip of every mode and of inactive settings, attribute escaping, and which
metadata combinations restore a seam mode.
trip of every mode and of inactive settings, attribute escaping, and the
metadata combinations that restore a seam mode.
[Plugin tests](../../tests/slic3rutils/test_precise_seam_plugin.cpp) cover the
Python bindings.
+25
View File
@@ -22855,3 +22855,28 @@ msgstr ""
#: resources/data/hints.ini: [hint:Avoid warping]
msgid "Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping?"
msgstr ""
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "failed to process some intersections (%1%)"
msgstr ""
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "multiple intersections with a perimeter, only one was used (%1%)"
msgstr ""
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "a perimeter is fully inside a modifier, the modifier was not applied to it (%1%)"
msgstr ""
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "modifier \"%1%\" of \"%2%\" had no effect on the seam (it might not reach the centerline of the printed perimeter)"
msgstr ""
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "modifier \"%1%\" of \"%2%\" (%3% in total) had no effect on the seam (it might not reach the centerline of the printed perimeter)"
msgstr ""
+3 -6
View File
@@ -74,11 +74,8 @@ src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp
src/slic3r/GUI/Gizmos/GLGizmoText.cpp
src/slic3r/GUI/Gizmos/GLGizmoText.hpp
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
@@ -177,7 +174,6 @@ src/slic3r/GUI/PresetHints.cpp
src/slic3r/GUI/ProgressStatusBar.cpp
src/slic3r/GUI/PlateSettingsDialog.cpp
src/slic3r/GUI/PrivacyUpdateDialog.cpp
src/slic3r/GUI/PublishDialog.cpp
src/slic3r/GUI/PublishSettingsDialog.cpp
src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/SceneBenchmark.cpp
@@ -196,7 +192,6 @@ src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UnsavedChangesDialog.cpp
src/slic3r/GUI/Auxiliary.cpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/ObjColorDialog.cpp
src/slic3r/GUI/SyncAmsInfoDialog.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
@@ -258,7 +253,6 @@ src/slic3r/Utils/MKS.cpp
src/slic3r/Utils/Moonraker.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/Repetier.cpp
src/slic3r/Utils/ProfileDescription.hpp
src/slic3r/GUI/SendMultiMachinePage.cpp
src/slic3r/GUI/MultiMachinePage.cpp
src/slic3r/GUI/MultiMachineManagerPage.cpp
@@ -299,7 +293,10 @@ src/slic3r/GUI/AMSDryControl.cpp
src/slic3r/GUI/AMSDryControl.hpp
src/libslic3r/PresetBundle.cpp
src/slic3r/GUI/CAD/DesignPanel.cpp
src/slic3r/GUI/CAD/DesignSketchTool.cpp
src/slic3r/GUI/CAD/SketchInlineEditor.cpp
src/slic3r/GUI/CAD/DesignOffer.hpp
src/slic3r/GUI/CAD/DesignTextDialog.cpp
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
src/slic3r/GUI/KeyChord.cpp
+21 -8
View File
@@ -27362,17 +27362,30 @@ msgstr ""
msgid "Precise Seam"
msgstr "Точный шов"
msgid "multiple intersections with a perimeter detected"
msgstr "обнаружены множественные пересечения с периметром"
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "failed to process some intersections (%1%)"
msgstr "не удалось обработать некоторые пересечения (%1%)"
msgid "modifier fully crosses the printable perimeter"
msgstr "модификатор пересекает печатаемый периметр насквозь"
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "multiple intersections with a perimeter, only one was used (%1%)"
msgstr "несколько пересечений с периметром, использовано только одно (%1%)"
msgid "modifier shape is not solid (has holes inside) and was ignored"
msgstr "форма модификатора не сплошная (имеет отверстия) и была проигнорирована"
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "a perimeter is fully inside a modifier, the modifier was not applied to it (%1%)"
msgstr "периметр целиком внутри модификатора, модификатор для него не применён (%1%)"
msgid "perimeter is fully contained inside modifier and was ignored"
msgstr "периметр полностью содержится внутри модификатора и был проигнорирован"
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "modifier \"%1%\" of \"%2%\" had no effect on the seam (it might not reach the centerline of the printed perimeter)"
msgstr "модификатор «%1%» модели «%2%» не повлиял на шов (возможно, он не достигает осевой линии печатаемого периметра)"
#: src/libslic3r/GCode/SeamPlacer.cpp
#, possible-boost-format
msgid "modifier \"%1%\" of \"%2%\" (%3% in total) had no effect on the seam (it might not reach the centerline of the printed perimeter)"
msgstr "модификатор «%1%» модели «%2%» (всего: %3%) не повлиял на шов (возможно, он не достигает осевой линии печатаемого периметра)"
msgid "Seam placement may differ from expected."
msgstr "Размещение шва может отличаться от ожидаемого."
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17 A11 11 0 0 1 20 17"/><circle cx="4" cy="17" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="7" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="20" cy="17" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17 A11 11 0 0 1 20 17"/><circle cx="4" cy="17" r="1.5" fill="#949494" stroke="none"/><circle cx="12" cy="7" r="1.5" fill="#949494" stroke="none"/><circle cx="20" cy="17" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 406 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 18 A13 13 0 0 1 18 5"/><path d="M5 5 5 18M5 5 18 5" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="5" cy="5" r="2" fill="#b6b6b6" stroke="none"/><circle cx="5" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="18" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 18 A13 13 0 0 1 18 5"/><path d="M5 5 5 18M5 5 18 5" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="5" cy="5" r="2" fill="#949494" stroke="none"/><circle cx="5" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="18" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 472 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="6" height="6"/><rect x="15" y="3" width="6" height="6"/><rect x="3" y="15" width="6" height="6"/><rect x="15" y="15" width="6" height="6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="6" height="6"/><rect x="15" y="3" width="6" height="6"/><rect x="3" y="15" width="6" height="6"/><rect x="15" y="15" width="6" height="6"/></svg>

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 350 B

+1 -1
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="9" cy="12" r="6"/>
<circle cx="15" cy="12" r="6"/>
</svg>

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 19 C 6 5, 12 5, 12 12 S 18 19, 21 5"/><circle cx="3" cy="19" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 19 C 6 5, 12 5, 12 12 S 18 19, 21 5"/><circle cx="3" cy="19" r="1.5" fill="#949494" stroke="none"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16M4 20 16 6"/><path d="M11 20 A7 7 0 0 0 9.1 15.2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h16M4 20 16 6"/><path d="M11 20 A7 7 0 0 0 9.1 15.2"/></svg>

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 254 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2.2" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2.2" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h20" stroke-dasharray="1.5 1.5"/><path d="M5 12h5M14 12h5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h20" stroke-dasharray="1.5 1.5"/><path d="M5 12h5M14 12h5"/></svg>

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 260 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3.5"/><circle cx="12" cy="12" r="1.3" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3.5"/><circle cx="12" cy="12" r="1.3" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 312 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7"/><path d="M6.3 17.7 17.7 6.3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7"/><path d="M6.3 17.7 17.7 6.3"/></svg>

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 247 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6v12M18 6v12M6 12h12M6 12l2.5-2M6 12l2.5 2M18 12l-2.5-2M18 12l-2.5 2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6v12M18 6v12M6 12h12M6 12l2.5-2M6 12l2.5 2M18 12l-2.5-2M18 12l-2.5 2"/></svg>

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6h12M6 18h12M12 6v12M12 6l-2 2.5M12 6l2 2.5M12 18l-2-2.5M12 18l2-2.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6h12M6 18h12M12 6v12M12 6l-2 2.5M12 6l2 2.5M12 18l-2-2.5M12 18l2-2.5"/></svg>

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9h14M5 15h14"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9h14M5 15h14"/></svg>

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="4" cy="12" r="2.5"/><circle cx="15" cy="12" r="4.5"/><path d="M8 10h2M8 14h2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="4" cy="12" r="2.5"/><circle cx="15" cy="12" r="4.5"/><path d="M8 10h2M8 14h2"/></svg>

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5.5" r="2"/><path d="M12 7.5V20M8.5 10h7M6 14a6 6 0 0 0 12 0"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5.5" r="2"/><path d="M12 7.5V20M8.5 10h7M6 14a6 6 0 0 0 12 0"/></svg>

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18M3 9v6M21 9v6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18M3 9v6M21 9v6"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16M4 9v6M20 9v6"/><circle cx="12" cy="12" r="2" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16M4 9v6M20 9v6"/><circle cx="12" cy="12" r="2" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20 13 4M13 20 19 4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20 13 4M13 20 19 4"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v15h15M6 13h5v5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v15h15M6 13h5v5"/></svg>

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 217 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><path d="M12 12h8M17.5 9.5 20 12 17.5 14.5"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><path d="M12 12h8M17.5 9.5 20 12 17.5 14.5"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 6h8M8 18h8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 6h8M8 18h8"/></svg>

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 272 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M6 8v8M18 8v8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M6 8v8M18 8v8"/></svg>

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 272 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 8 4 12 8 16M16 8l4 4-4 4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 8 4 12 8 16M16 8l4 4-4 4"/></svg>

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="14.5" r="5"/><path d="M3 9.5h18"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="14.5" r="5"/><path d="M3 9.5h18"/></svg>

Before

Width:  |  Height:  |  Size: 240 B

After

Width:  |  Height:  |  Size: 240 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M9 3h6M9 21h6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M9 3h6M9 21h6"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8l6 6h8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8l6 6h8"/></svg>

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 210 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l4 4L19 6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l4 4L19 6"/></svg>

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 212 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7.5"/><path d="M6.7 6.7 17.3 17.3" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="6.7" cy="6.7" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="17.3" cy="17.3" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7.5"/><path d="M6.7 6.7 17.3 17.3" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="6.7" cy="6.7" r="1.5" fill="#949494" stroke="none"/><circle cx="17.3" cy="17.3" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="4" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="5.07" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="18.93" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="4" r="1.5" fill="#949494" stroke="none"/><circle cx="5.07" cy="16" r="1.5" fill="#949494" stroke="none"/><circle cx="18.93" cy="16" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 407 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 270 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" stroke-dasharray="3 3"/><circle cx="4" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="20" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" stroke-dasharray="3 3"/><circle cx="4" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="20" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 297 B

+1 -1
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M6 8.5l6-3 6 3v7l-6 3-6-3z"/>
<line x1="3" y1="14" x2="21" y2="10"/>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6"/></svg>

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 248 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7v10M20 7v10M4 12h16M7 9l-3 3 3 3M17 9l3 3-3 3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7v10M20 7v10M4 12h16M7 9l-3 3 3 3M17 9l3 3-3 3"/></svg>

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 247 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4H18L15 20H9Z"/><path d="M9 20V4" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M9 14a4 4 0 0 0 1.3 3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4H18L15 20H9Z"/><path d="M9 20V4" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M9 14a4 4 0 0 0 1.3 3"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20V10a6 6 0 0 1 6-6h10"/><path d="M4 20h4M20 4v4" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20V10a6 6 0 0 1 6-6h10"/><path d="M4 20h4M20 4v4" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M14 6l4 4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M14 6l4 4"/></svg>

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="9.5" ry="5.5"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="9.5" ry="5.5"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 292 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13 A9.5 5.5 0 0 1 21 10"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="3" cy="13" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="10" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13 A9.5 5.5 0 0 1 21 10"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="3" cy="13" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="10" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 409 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h11"/><path d="M14 12h7m-3-3l3 3-3 3"/><path d="M14 12h7" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h11"/><path d="M14 12h7m-3-3l3 3-3 3"/><path d="M14 12h7" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="11" width="16" height="9" rx="1"/><path d="M12 8V2M9 5l3-3 3 3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="11" width="16" height="9" rx="1"/><path d="M12 8V2M9 5l3-3 3 3"/></svg>

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C5 6 19 6 22 12 19 18 5 18 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C5 6 19 6 22 12 19 18 5 18 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M2 8.5C5 14.5 19 14.5 22 8.5"/><path d="M12 13v3M5.97 11.9l-1.4 2.5M18.03 11.9l1.4 2.5M3.2 10.1l-1.8 1.8M20.8 10.1l1.8 1.8"/></svg>

After

Width:  |  Height:  |  Size: 320 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8a6 6 0 0 0 6 6h8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5v8a6 6 0 0 0 6 6h8"/></svg>

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 328 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 19 5"/><circle cx="5" cy="19" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="19" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 19 5"/><circle cx="5" cy="19" r="1.5" fill="#949494" stroke="none"/><circle cx="19" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 330 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="15.5" width="8.5" height="4.5" rx="0.5"/><ellipse cx="16.5" cy="6" rx="4" ry="1.7"/><path d="M3.2 15.5 12.6 4.8M11.4 20 20.4 7.2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="15.5" width="8.5" height="4.5" rx="0.5"/><ellipse cx="16.5" cy="6" rx="4" ry="1.7"/><path d="M3.2 15.5 12.6 4.8M11.4 20 20.4 7.2"/></svg>

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 7 4 12 8 17Z"/><path d="M16 7l4 5-4 5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18" stroke-dasharray="2.5 2.5" opacity="0.5"/><path d="M8 7 4 12 8 17Z"/><path d="M16 7l4 5-4 5Z"/></svg>

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M12 8V2M9.6 4.4 12 2 14.4 4.4M12 16v6M9.6 19.6 12 22 14.4 19.6M8 12H2M4.4 9.6 2 12 4.4 14.4M16 12h6M19.6 9.6 22 12 19.6 14.4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M12 8V2M9.6 4.4 12 2 14.4 4.4M12 16v6M9.6 19.6 12 22 14.4 19.6M8 12H2M4.4 9.6 2 12 4.4 14.4M16 12h6M19.6 9.6 22 12 19.6 14.4"/></svg>

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 369 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M6 13l6 6 6-6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M6 13l6 6 6-6"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M6 11l6-6 6 6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M6 11l6-6 6 6"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 20V10A7 7 0 0 1 10 3H20"/><path d="M7 20V10A3 3 0 0 1 10 7H20"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 20V10A7 7 0 0 1 10 3H20"/><path d="M7 20V10A3 3 0 0 1 10 7H20"/></svg>

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 262 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="9" width="6" height="6" rx="1"/><rect x="10.5" y="9" width="6" height="6" rx="1"/><rect x="18" y="9" width="3.6" height="6" rx="1" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="9" width="6" height="6" rx="1"/><rect x="10.5" y="9" width="6" height="6" rx="1"/><rect x="18" y="9" width="3.6" height="6" rx="1" stroke-dasharray="2.5 2.5" opacity="0.5"/></svg>

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 374 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8 13 8 21 15 11 15Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8 13 8 21 15 11 15Z"/></svg>

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v3.4M12 16.6v3.4M4 12h3.4M16.6 12h3.4"/><circle cx="12" cy="12" r="2.2" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v3.4M12 16.6v3.4M4 12h3.4M16.6 12h3.4"/><circle cx="12" cy="12" r="2.2" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 302 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="12" cy="12" r="1.4" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="4" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="18.9" cy="8" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="18.9" cy="16" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="12" cy="20" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="5.1" cy="16" r="1.8" fill="#b6b6b6" stroke="none"/><circle cx="5.1" cy="8" r="1.8" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8" stroke-dasharray="2.5 2.5" opacity="0.5"/><circle cx="12" cy="12" r="1.4" fill="#949494" stroke="none"/><circle cx="12" cy="4" r="1.8" fill="#949494" stroke="none"/><circle cx="18.9" cy="8" r="1.8" fill="#949494" stroke="none"/><circle cx="18.9" cy="16" r="1.8" fill="#949494" stroke="none"/><circle cx="12" cy="20" r="1.8" fill="#949494" stroke="none"/><circle cx="5.1" cy="16" r="1.8" fill="#949494" stroke="none"/><circle cx="5.1" cy="8" r="1.8" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 695 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9Z"/><circle cx="12" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9Z"/><circle cx="12" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18 9 9 14 15 21 5"/><circle cx="3" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="9" cy="9" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="14" cy="15" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18 9 9 14 15 21 5"/><circle cx="3" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="9" cy="9" r="1.5" fill="#949494" stroke="none"/><circle cx="14" cy="15" r="1.5" fill="#949494" stroke="none"/><circle cx="21" cy="5" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 462 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="4" cy="6" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="1"/><circle cx="4" cy="6" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 295 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 16 10 6 20 8 14 18Z"/><circle cx="4" cy="16" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="10" cy="6" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M4 16 10 6 20 8 14 18Z"/><circle cx="4" cy="16" r="1.5" fill="#949494" stroke="none"/><circle cx="10" cy="6" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 342 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="6" width="17" height="12" rx="4.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3.5" y="6" width="17" height="12" rx="4.5"/></svg>

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 16.5 A9 9 0 0 1 21 16.5 L16 16.5 A4 4 0 0 0 8 16.5 Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 16.5 A9 9 0 0 1 21 16.5 L16 16.5 A4 4 0 0 0 8 16.5 Z"/></svg>

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="18" width="18" height="3" rx="0.5"/><rect x="6" y="6" width="2.5" height="12" rx="0.5"/><path d="M8.5 9L17 18"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="18" width="18" height="3" rx="0.5"/><rect x="6" y="6" width="2.5" height="12" rx="0.5"/><path d="M8.5 9L17 18"/></svg>

Before

Width:  |  Height:  |  Size: 313 B

After

Width:  |  Height:  |  Size: 313 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3.5-7.1"/><path d="M21 3 21 8 16 8"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3.5-7.1"/><path d="M21 3 21 8 16 8"/></svg>

Before

Width:  |  Height:  |  Size: 249 B

After

Width:  |  Height:  |  Size: 249 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M8 8 3 3M3 7 3 3 7 3M16 8 21 3M17 3 21 3 21 7M8 16 3 21M3 17 3 21 7 21M16 16 21 21M17 21 21 21 21 17"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="1"/><path d="M8 8 3 3M3 7 3 3 7 3M16 8 21 3M17 3 21 3 21 7M8 16 3 21M3 17 3 21 7 21M16 16 21 21M17 21 21 21 21 17"/></svg>

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3 5 17.5 8.8 14.2 11.3 20.3 13.4 19.4 11 13.6 15.9 13.6Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3 5 17.5 8.8 14.2 11.3 20.3 13.4 19.4 11 13.6 15.9 13.6Z"/></svg>

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 257 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="1"/><path d="M8 4v12h8V4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="1"/><path d="M8 4v12h8V4"/></svg>

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M16 3.5a2.1 2.1 0 0 1 3 3L7.5 18l-4 1 1-4Z"/><path d="M14.5 5 19 9.5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M16 3.5a2.1 2.1 0 0 1 3 3L7.5 18l-4 1 1-4Z"/><path d="M14.5 5 19 9.5"/></svg>

Before

Width:  |  Height:  |  Size: 266 B

After

Width:  |  Height:  |  Size: 266 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 7h8a5 5 0 0 1 0 10H8a5 5 0 0 1 0-10Z"/><circle cx="8" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="16" cy="12" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 7h8a5 5 0 0 1 0 10H8a5 5 0 0 1 0-10Z"/><circle cx="8" cy="12" r="1.5" fill="#949494" stroke="none"/><circle cx="16" cy="12" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 360 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5.3 13 A8 8 0 0 1 12.9 17 A2.5 2.5 0 0 0 17.3 14.5 A13 13 0 0 0 4.9 8 A2.5 2.5 0 0 0 5.3 13Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M5.3 13 A8 8 0 0 1 12.9 17 A2.5 2.5 0 0 0 17.3 14.5 A13 13 0 0 0 4.9 8 A2.5 2.5 0 0 0 5.3 13Z"/></svg>

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.0 9.0 L16.0 9.0 L16.0 21.0 L3.0 21.0 ZM3.0 9.0 L7.0 5.0 L20.0 5.0 L16.0 9.0M20.0 5.0 L20.0 17.0 L16.0 21.0" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.168410256410256 13.423897435897436V13.868410256410256Q5.908923076923077 13.744307692307691 5.678769230769231 13.683384615384615Q5.448615384615385 13.622461538461538 5.23425641025641 13.622461538461538Q4.861948717948718 13.622461538461538 4.66 13.766871794871793Q4.458051282051283 13.91128205128205 4.458051282051283 14.177538461538461Q4.458051282051283 14.400923076923076 4.592307692307693 14.514871794871794Q4.726564102564103 14.628820512820512 5.101128205128205 14.69876923076923L5.3764102564102565 14.755179487179486Q5.886358974358975 14.852205128205128 6.128923076923078 15.09702564102564Q6.37148717948718 15.341846153846152 6.37148717948718 15.75251282051282Q6.37148717948718 16.242153846153844 6.043179487179488 16.49487179487179Q5.7148717948717955 16.747589743589742 5.080820512820513 16.747589743589742Q4.841641025641026 16.747589743589742 4.572000000000001 16.693435897435897Q4.302358974358975 16.639282051282052 4.013538461538462 16.53323076923077V16.063897435897434Q4.291076923076924 16.219589743589744 4.557333333333334 16.2985641025641Q4.823589743589745 16.37753846153846 5.080820512820513 16.37753846153846Q5.471179487179487 16.37753846153846 5.683282051282052 16.224102564102562Q5.895384615384616 16.070666666666664 5.895384615384616 15.786358974358974Q5.895384615384616 15.538153846153845 5.743076923076924 15.398256410256408Q5.590769230769231 15.258358974358973 5.243282051282051 15.188410256410256L4.965743589743591 15.134256410256409Q4.455794871794872 15.032717948717949 4.227897435897436 14.816102564102565Q4.000000000000001 14.599487179487179 4.000000000000001 14.213641025641024Q4.000000000000001 13.766871794871793 4.314769230769231 13.509641025641024Q4.629538461538462 13.252410256410256 5.182358974358975 13.252410256410256Q5.419282051282051 13.252410256410256 5.665230769230769 13.29528205128205Q5.911179487179488 13.338153846153846 6.168410256410256 13.423897435897436ZM6.615179487179487 13.313333333333333H9.46502564102564V13.696923076923076H8.269128205128204V16.682153846153845H7.811076923076923V13.696923076923076H6.615179487179487ZM9.905025641025642 13.313333333333333H12.035076923076925V13.696923076923076H10.360820512820514V14.69425641025641H11.965128205128206V15.077846153846153H10.360820512820514V16.2985641025641H12.075692307692309V16.682153846153845H9.905025641025642ZM13.280615384615384 13.687897435897435V14.953743589743588H13.85374358974359Q14.171897435897435 14.953743589743588 14.345641025641026 14.78902564102564Q14.519384615384615 14.624307692307692 14.519384615384615 14.319692307692307Q14.519384615384615 14.017333333333333 14.345641025641026 13.852615384615383Q14.171897435897435 13.687897435897435 13.85374358974359 13.687897435897435ZM12.824820512820512 13.313333333333333H13.85374358974359Q14.420102564102564 13.313333333333333 14.710051282051282 13.569435897435897Q15.0 13.82553846153846 15.0 14.319692307692307Q15.0 14.818358974358974 14.710051282051282 15.073333333333332Q14.420102564102564 15.32830769230769 13.85374358974359 15.32830769230769H13.280615384615384V16.682153846153845H12.824820512820512Z" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 14C7 10 11 18 15 14L21 9C17 13 13 5 9 9Z"/><path d="M6 11.5 12 6.5" stroke-opacity="0.55"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 14C7 10 11 18 15 14L21 9C17 13 13 5 9 9Z"/><path d="M6 11.5 12 6.5" stroke-opacity="0.55"/></svg>

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7.081932245922209 8.669636135508153V9.608908406524465Q6.5336260978670015 9.346675031367628 6.047302383939774 9.217942283563362Q5.560978670012547 9.089209535759096 5.108030112923463 9.089209535759096Q4.321329987452948 9.089209535759096 3.894604767879548 9.39435382685069Q3.467879548306148 9.699498117942282 3.467879548306148 10.262107904642408Q3.467879548306148 10.734127979924716 3.751568381430364 10.974905897114176Q4.03525721455458 11.215683814303638 4.8267252195734 11.363488080301128L5.40840652446675 11.482685069008781Q6.48594730238394 11.687703889585945 6.9984943538268505 12.205018820577163Q7.511041405269761 12.72233375156838 7.511041405269761 13.5900878293601Q7.511041405269761 14.624717691342532 6.817314930991217 15.158720200752821Q6.123588456712673 15.69272271016311 4.7838143036386445 15.69272271016311Q4.278419071518194 15.69272271016311 3.708657465495609 15.578293601003763Q3.138895859473024 15.463864491844415 2.528607277289837 15.239774153074025V14.248055207026347Q3.115056461731493 14.577038895859472 3.6776662484316187 14.743914680050187Q4.240276035131744 14.910790464240902 4.7838143036386445 14.910790464240902Q5.608657465495608 14.910790464240902 6.056838143036387 14.586574654956085Q6.505018820577165 14.262358845671265 6.505018820577165 13.66160602258469Q6.505018820577165 13.137139272271014 6.183186951066499 12.841530740276033Q5.861355081555834 12.545922208281052 5.1271016311166875 12.398117942283562L4.540652446675031 12.283688833124215Q3.4631116687578416 12.069134253450438 2.981555834378921 11.611417816813049Q2.5 11.153701380175658 2.5 10.338393977415306Q2.5 9.39435382685069 3.165119196988708 8.850815558343788Q3.830238393977415 8.307277289836886 4.998368883312422 8.307277289836886Q5.498996235884567 8.307277289836886 6.018695106649937 8.397867001254703Q6.538393977415307 8.48845671267252 7.081932245922209 8.669636135508153ZM10.848557089084066 15.554454203262232 8.13086574654956 8.436010037641154H9.136888331242158L11.392095357590966 14.42923462986198L13.652070263488081 8.436010037641154H14.653324968632372L11.940401505646173 15.554454203262232ZM20.546424090338768 14.538895859473023V12.626976160602258H18.97302383939774V11.835508155583437H21.499999999999996V14.891718946047677Q20.942158092848178 15.287452948557087 20.26988707653701 15.4900878293601Q19.597616060225846 15.69272271016311 18.83475533249686 15.69272271016311Q17.16599749058971 15.69272271016311 16.224341279799248 14.717691342534504Q15.282685069008783 13.742659974905896 15.282685069008783 12.002383939774152Q15.282685069008783 10.257340025094102 16.224341279799248 9.282308657465494Q17.16599749058971 8.307277289836886 18.83475533249686 8.307277289836886Q19.53086574654956 8.307277289836886 20.15784190715182 8.478920953575908Q20.784818067754077 8.650564617314929 21.31405269761606 8.984316185696361V10.009410288582181Q20.78005018820577 9.556461731493098 20.179297365119197 9.327603513174402Q19.57854454203262 9.098745294855707 18.915809284818067 9.098745294855707Q17.609410288582183 9.098745294855707 16.953826850690085 9.828230865746548Q16.29824341279799 10.557716436637389 16.29824341279799 12.002383939774152Q16.29824341279799 13.442283563362608 16.953826850690085 14.171769134253449Q17.609410288582183 14.90125470514429 18.915809284818067 14.90125470514429Q19.425972396486824 14.90125470514429 19.826474278544538 14.813048933500625Q20.226976160602256 14.724843161856962 20.546424090338768 14.538895859473023Z" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M7.081932245922209 8.669636135508153V9.608908406524465Q6.5336260978670015 9.346675031367628 6.047302383939774 9.217942283563362Q5.560978670012547 9.089209535759096 5.108030112923463 9.089209535759096Q4.321329987452948 9.089209535759096 3.894604767879548 9.39435382685069Q3.467879548306148 9.699498117942282 3.467879548306148 10.262107904642408Q3.467879548306148 10.734127979924716 3.751568381430364 10.974905897114176Q4.03525721455458 11.215683814303638 4.8267252195734 11.363488080301128L5.40840652446675 11.482685069008781Q6.48594730238394 11.687703889585945 6.9984943538268505 12.205018820577163Q7.511041405269761 12.72233375156838 7.511041405269761 13.5900878293601Q7.511041405269761 14.624717691342532 6.817314930991217 15.158720200752821Q6.123588456712673 15.69272271016311 4.7838143036386445 15.69272271016311Q4.278419071518194 15.69272271016311 3.708657465495609 15.578293601003763Q3.138895859473024 15.463864491844415 2.528607277289837 15.239774153074025V14.248055207026347Q3.115056461731493 14.577038895859472 3.6776662484316187 14.743914680050187Q4.240276035131744 14.910790464240902 4.7838143036386445 14.910790464240902Q5.608657465495608 14.910790464240902 6.056838143036387 14.586574654956085Q6.505018820577165 14.262358845671265 6.505018820577165 13.66160602258469Q6.505018820577165 13.137139272271014 6.183186951066499 12.841530740276033Q5.861355081555834 12.545922208281052 5.1271016311166875 12.398117942283562L4.540652446675031 12.283688833124215Q3.4631116687578416 12.069134253450438 2.981555834378921 11.611417816813049Q2.5 11.153701380175658 2.5 10.338393977415306Q2.5 9.39435382685069 3.165119196988708 8.850815558343788Q3.830238393977415 8.307277289836886 4.998368883312422 8.307277289836886Q5.498996235884567 8.307277289836886 6.018695106649937 8.397867001254703Q6.538393977415307 8.48845671267252 7.081932245922209 8.669636135508153ZM10.848557089084066 15.554454203262232 8.13086574654956 8.436010037641154H9.136888331242158L11.392095357590966 14.42923462986198L13.652070263488081 8.436010037641154H14.653324968632372L11.940401505646173 15.554454203262232ZM20.546424090338768 14.538895859473023V12.626976160602258H18.97302383939774V11.835508155583437H21.499999999999996V14.891718946047677Q20.942158092848178 15.287452948557087 20.26988707653701 15.4900878293601Q19.597616060225846 15.69272271016311 18.83475533249686 15.69272271016311Q17.16599749058971 15.69272271016311 16.224341279799248 14.717691342534504Q15.282685069008783 13.742659974905896 15.282685069008783 12.002383939774152Q15.282685069008783 10.257340025094102 16.224341279799248 9.282308657465494Q17.16599749058971 8.307277289836886 18.83475533249686 8.307277289836886Q19.53086574654956 8.307277289836886 20.15784190715182 8.478920953575908Q20.784818067754077 8.650564617314929 21.31405269761606 8.984316185696361V10.009410288582181Q20.78005018820577 9.556461731493098 20.179297365119197 9.327603513174402Q19.57854454203262 9.098745294855707 18.915809284818067 9.098745294855707Q17.609410288582183 9.098745294855707 16.953826850690085 9.828230865746548Q16.29824341279799 10.557716436637389 16.29824341279799 12.002383939774152Q16.29824341279799 13.442283563362608 16.953826850690085 14.171769134253449Q17.609410288582183 14.90125470514429 18.915809284818067 14.90125470514429Q19.425972396486824 14.90125470514429 19.826474278544538 14.813048933500625Q20.226976160602256 14.724843161856962 20.546424090338768 14.538895859473023Z" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="17" r="2.4"/><path d="M6 17 C 7 9, 15 15, 18 6"/><path d="M15.2 6.6 18 6 17.7 8.9"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="17" r="2.4"/><path d="M6 17 C 7 9, 15 15, 18 6"/><path d="M15.2 6.6 18 6 17.7 8.9"/></svg>

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18h8"/><path d="M11 18 A7 7 0 0 1 18 11"/><circle cx="3" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/><circle cx="11" cy="18" r="1.5" fill="#b6b6b6" stroke="none"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 18h8"/><path d="M11 18 A7 7 0 0 1 18 11"/><circle cx="3" cy="18" r="1.5" fill="#949494" stroke="none"/><circle cx="11" cy="18" r="1.5" fill="#949494" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 20 12 4 18 20M8.5 14h7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M6 20 12 4 18 20M8.5 14h7"/></svg>

Before

Width:  |  Height:  |  Size: 223 B

After

Width:  |  Height:  |  Size: 223 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10C7 6 11 14 15 10L21 5"/><path d="M3 16C7 12 11 20 15 16L21 11"/><path d="M3 10v6M21 5v6"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10C7 6 11 14 15 10L21 5"/><path d="M3 16C7 12 11 20 15 16L21 11"/><path d="M3 10v6M21 5v6"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3h8v13l-4 5-4-5Z"/><path d="M8 7l8-2M8 11l8-2M8 15l8-2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3h8v13l-4 5-4-5Z"/><path d="M8 7l8-2M8 11l8-2M8 15l8-2"/></svg>

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 255 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#b6b6b6" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6.5" r="2.6"/><circle cx="6" cy="17.5" r="2.6"/><path d="M8.3 8 20.5 17M8.3 16 20.5 7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#949494" stroke-width="0.85" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6.5" r="2.6"/><circle cx="6" cy="17.5" r="2.6"/><path d="M8.3 8 20.5 17M8.3 16 20.5 7"/></svg>

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 293 B

Some files were not shown because too many files have changed in this diff Show More