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)
@@ -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 →
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,117 @@ 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.
|
||||
|
||||
## 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 +290,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 |
|
||||
|
||||
@@ -293,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
|
||||
|
||||
@@ -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 @@
|
||||
<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 @@
|
||||
<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,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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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,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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 |
@@ -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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 @@
|
||||
<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 |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M33.5,4H5.5A1.5,1.5,0,0,0,4,5.5v28A1.5,1.5,0,0,0,5.5,35h16a.5.5,0,0,0,0-1H5.5a.5.5,0,0,1-.5-.5V5.5A.5.5,0,0,1,5.5,5h28a.5.5,0,0,1,.5.5v16a.5.5,0,0,0,1,0V5.5A1.5,1.5,0,0,0,33.5,4Z" style="fill:#2b3436"/><path d="M19.5,12.5H32.5M6.5,19.5H32.5M6.5,26.5H21.5M12.5,19.5V32.5M19.5,6.5V32.5M26.5,6.5V21.5" style="fill:none;stroke:#2b3436;stroke-linecap:round"/><ellipse cx="12" cy="8.5" rx="5.5" ry="2" style="fill:none;stroke:#009688"/><path d="M6.5,8.5v7a5.5,2,0,0,0,11,0v-7" style="fill:none;stroke:#009688;stroke-linecap:round;stroke-linejoin:round"/><path d="M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z" style="fill:#009688"/><path d="M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/></svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M33.5,4H5.5A1.5,1.5,0,0,0,4,5.5v28A1.5,1.5,0,0,0,5.5,35h16a.5.5,0,0,0,0-1H5.5a.5.5,0,0,1-.5-.5V5.5A.5.5,0,0,1,5.5,5h28a.5.5,0,0,1,.5.5v16a.5.5,0,0,0,1,0V5.5A1.5,1.5,0,0,0,33.5,4Z" style="fill:#b6b6b6"/><path d="M19.5,12.5H32.5M6.5,19.5H32.5M6.5,26.5H21.5M12.5,19.5V32.5M19.5,6.5V32.5M26.5,6.5V21.5" style="fill:none;stroke:#b6b6b6;stroke-linecap:round"/><ellipse cx="12" cy="8.5" rx="5.5" ry="2" style="fill:none;stroke:#009688"/><path d="M6.5,8.5v7a5.5,2,0,0,0,11,0v-7" style="fill:none;stroke:#009688;stroke-linecap:round;stroke-linejoin:round"/><path d="M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z" style="fill:#009688"/><path d="M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/></svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
@@ -64,6 +64,10 @@ uniform PrintVolumeDetection print_volume;
|
||||
uniform float z_far;
|
||||
uniform float z_near;
|
||||
uniform bool enable_ssao;
|
||||
// 1 = the Design tab's studio lighting (studio_shade below); 0 = the two-light model above, which
|
||||
// every other canvas keeps. world_up_eye is world +Z in eye space, for the hemisphere ambient.
|
||||
uniform int lighting_model;
|
||||
uniform vec3 world_up_eye;
|
||||
|
||||
// Depth-based shadow map (object-on-object and self shadows). shadow_intensity == 0 disables it.
|
||||
uniform sampler2D shadow_map;
|
||||
@@ -210,6 +214,27 @@ float shadow_shade()
|
||||
return 1.0 - shadow_intensity * (sum / 25.0);
|
||||
}
|
||||
|
||||
// Studio lighting for the Design tab. The default model lights every face from near the camera,
|
||||
// so the sides of a part come out in nearly the same tone and its form is hard to read. This one
|
||||
// separates faces by their orientation in the WORLD (a sky/ground hemisphere: up-facing faces
|
||||
// cool and bright, down-facing ones warm and dark), keeps a strong key light from the upper left
|
||||
// and a weak fill from the right, gives a plastic-like highlight, and darkens the base colour
|
||||
// toward the silhouette while adding a faint sheen there, so curved faces read as round.
|
||||
vec3 studio_shade(vec3 base, vec3 n, vec3 v)
|
||||
{
|
||||
vec3 key = normalize(vec3(-0.45, 0.60, 0.66));
|
||||
vec3 fill = normalize(vec3(0.70, -0.15, 0.70));
|
||||
float hemi = 0.5 + 0.5 * dot(n, normalize(world_up_eye));
|
||||
vec3 ambient = mix(vec3(0.16, 0.15, 0.14), vec3(0.40, 0.42, 0.46), hemi);
|
||||
float kd = max(dot(n, key), 0.0);
|
||||
float fd = max(dot(n, fill), 0.0);
|
||||
vec3 diffuse = ambient + vec3(0.60) * kd + vec3(0.20) * fd;
|
||||
float spec = 0.28 * pow(max(dot(n, normalize(key + v)), 0.0), 48.0)
|
||||
+ 0.06 * pow(max(dot(n, normalize(fill + v)), 0.0), 24.0);
|
||||
float rim = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 3.0);
|
||||
return base * diffuse * (1.0 - 0.30 * rim) + vec3(spec + 0.08 * rim);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@@ -266,9 +291,11 @@ void main()
|
||||
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
|
||||
|
||||
float shade = shadow_shade();
|
||||
vec3 lit = (lighting_model == 1) ? studio_shade(color.rgb, normal, view_dir)
|
||||
: (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
|
||||
|
||||
if (is_outline) {
|
||||
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade;
|
||||
vec3 shaded_rgb = lit * shade;
|
||||
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
vec2 fragCoord = gl_FragCoord.xy;
|
||||
float s = DetectSilho(fragCoord);
|
||||
@@ -286,5 +313,5 @@ void main()
|
||||
gl_FragColor = vec4(clamp((0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
#endif
|
||||
else
|
||||
gl_FragColor = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
gl_FragColor = vec4(clamp(lit * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
||||
@@ -74,6 +74,10 @@ uniform PrintVolumeDetection print_volume;
|
||||
uniform float z_far;
|
||||
uniform float z_near;
|
||||
uniform bool enable_ssao;
|
||||
// 1 = the Design tab's studio lighting (studio_shade below); 0 = the two-light model above, which
|
||||
// every other canvas keeps. world_up_eye is world +Z in eye space, for the hemisphere ambient.
|
||||
uniform int lighting_model;
|
||||
uniform vec3 world_up_eye;
|
||||
|
||||
// Depth-based shadow map (object-on-object and self shadows). shadow_intensity == 0 disables it.
|
||||
uniform sampler2D shadow_map;
|
||||
@@ -267,6 +271,27 @@ float shadow_shade()
|
||||
return 1.0 - shadow_intensity * (sum / 25.0);
|
||||
}
|
||||
|
||||
// Studio lighting for the Design tab. The default model lights every face from near the camera,
|
||||
// so the sides of a part come out in nearly the same tone and its form is hard to read. This one
|
||||
// separates faces by their orientation in the WORLD (a sky/ground hemisphere: up-facing faces
|
||||
// cool and bright, down-facing ones warm and dark), keeps a strong key light from the upper left
|
||||
// and a weak fill from the right, gives a plastic-like highlight, and darkens the base colour
|
||||
// toward the silhouette while adding a faint sheen there, so curved faces read as round.
|
||||
vec3 studio_shade(vec3 base, vec3 n, vec3 v)
|
||||
{
|
||||
vec3 key = normalize(vec3(-0.45, 0.60, 0.66));
|
||||
vec3 fill = normalize(vec3(0.70, -0.15, 0.70));
|
||||
float hemi = 0.5 + 0.5 * dot(n, normalize(world_up_eye));
|
||||
vec3 ambient = mix(vec3(0.16, 0.15, 0.14), vec3(0.40, 0.42, 0.46), hemi);
|
||||
float kd = max(dot(n, key), 0.0);
|
||||
float fd = max(dot(n, fill), 0.0);
|
||||
vec3 diffuse = ambient + vec3(0.60) * kd + vec3(0.20) * fd;
|
||||
float spec = 0.28 * pow(max(dot(n, normalize(key + v)), 0.0), 48.0)
|
||||
+ 0.06 * pow(max(dot(n, normalize(fill + v)), 0.0), 24.0);
|
||||
float rim = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 3.0);
|
||||
return base * diffuse * (1.0 - 0.30 * rim) + vec3(spec + 0.08 * rim);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
@@ -323,9 +348,11 @@ void main()
|
||||
// SSAO is applied in post-process pass. Keep base lighting unchanged here.
|
||||
|
||||
float shade = shadow_shade();
|
||||
vec3 lit = (lighting_model == 1) ? studio_shade(color.rgb, normal, view_dir)
|
||||
: (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS;
|
||||
|
||||
if (is_outline) {
|
||||
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade;
|
||||
vec3 shaded_rgb = lit * shade;
|
||||
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
|
||||
float s = DetectSilho(gl_FragCoord.xy);
|
||||
if (s < 0.01)
|
||||
@@ -337,5 +364,5 @@ void main()
|
||||
out_color = vec4(clamp((0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + window_reflection + 0.8 * color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
#endif
|
||||
else
|
||||
out_color = vec4(clamp((vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
out_color = vec4(clamp(lit * shade, vec3(0.0), vec3(1.0)), color.a);
|
||||
}
|
||||
@@ -95,7 +95,8 @@ def load_table():
|
||||
f.append(cur.strip())
|
||||
if len(f) < 12:
|
||||
continue
|
||||
lit = lambda s: None if s == "nullptr" else s.strip('"')
|
||||
# User-facing fields are wrapped in the L("...") gettext marker; strip it.
|
||||
lit = lambda s: None if s == "nullptr" else re.sub(r'^L\((.*)\)$', r'\1', s).strip('"')
|
||||
out.append({"id": lit(f[0]), "name": lit(f[1]), "row": int(f[2]), "key": lit(f[3]),
|
||||
"action": lit(f[4]), "accepts": int(f[6].rstrip("u"), 0),
|
||||
"need_bodies": int(f[7]), "need_sketches": int(f[8]),
|
||||
|
||||
@@ -37,6 +37,12 @@ def cstr(s):
|
||||
return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
|
||||
|
||||
def tstr(s):
|
||||
# User-facing text: wrapped in the no-op L() marker so xgettext puts it in the catalogue.
|
||||
# The DesignPanel translates it at use; without the marker it compiles and never translates.
|
||||
return "nullptr" if s is None else "L(" + cstr(s) + ")"
|
||||
|
||||
|
||||
def validate(A):
|
||||
"""Refuse an atlas the header cannot represent, naming every fault at once.
|
||||
|
||||
@@ -81,6 +87,10 @@ def main():
|
||||
"",
|
||||
"#include <cstdint>",
|
||||
"",
|
||||
"#ifndef L",
|
||||
"#define L(s) s // gettext marker, as in slic3r/GUI/I18N.hpp",
|
||||
"#endif",
|
||||
"",
|
||||
"namespace Slic3r { namespace GUI {",
|
||||
"",
|
||||
"// What the viewport has selected. Ordered as in tool_atlas.json; the bitmask in",
|
||||
@@ -103,8 +113,8 @@ def main():
|
||||
"// nullptr -> kernel support exists, no GUI path yet (row shows disabled)",
|
||||
"struct OfferVerb {",
|
||||
" const char* id;",
|
||||
" const char* name; // drawing-office word (L10); translated at use with wxGetTranslation",
|
||||
" int row; // 0..7, the ratified index — NEVER reorder",
|
||||
" const char* name; // drawing-office word (L10); marked L(); translated at use",
|
||||
" int row; // index into kOfferRowNames, the ratified address — NEVER reorder",
|
||||
" const char* key; // shortcut shown in the row, or nullptr",
|
||||
" const char* action;",
|
||||
" const char* refusal; // why this row is greyed, in the product's own words",
|
||||
@@ -125,11 +135,20 @@ def main():
|
||||
"// Row labels, in ratified order.",
|
||||
"static const char* const kOfferRowNames[] = {",
|
||||
]
|
||||
# A flat row's label is never shown, so it is not marked for translation.
|
||||
for s in A["slots"]:
|
||||
lines.append(f' "{s["label"]}",')
|
||||
lines.append(f' {cstr(s["label"]) if s.get("flat") else tstr(s["label"])},')
|
||||
lines += [
|
||||
"};",
|
||||
f"static const int kOfferRowCount = {len(slots)};",
|
||||
"// A flat row is not a family: its verbs come first, at the top level of the offer, each",
|
||||
"// an item of its own.",
|
||||
"static const bool kOfferRowFlat[] = {",
|
||||
]
|
||||
for s in A["slots"]:
|
||||
lines.append(f' {"true" if s.get("flat") else "false"},')
|
||||
lines += [
|
||||
"};",
|
||||
"",
|
||||
"static const OfferVerb kOfferVerbs[] = {",
|
||||
]
|
||||
@@ -151,12 +170,12 @@ def main():
|
||||
n = v.get("needs") or {}
|
||||
lines.append(
|
||||
" {%s, %s, %d, %s, %s, %s, 0x%08xu, %d, %d, %s, %s, %s, %s, %s}," % (
|
||||
cstr(v["id"]), cstr(v["name"]), slots.index(v["slot"]),
|
||||
cstr(v.get("key")), cstr(v.get("action")), cstr(v.get("refusal")),
|
||||
cstr(v["id"]), tstr(v["name"]), slots.index(v["slot"]),
|
||||
cstr(v.get("key")), cstr(v.get("action")), tstr(v.get("refusal")),
|
||||
mask, n.get("bodies", 0), n.get("sketches", 0),
|
||||
"true" if n.get("sheet") else "false",
|
||||
"true" if v.get("mode") == "sketch" else "false",
|
||||
cstr(v.get("family")), cstr(v.get("icon")), cstr(v.get("hint"))))
|
||||
tstr(v.get("family")), cstr(v.get("icon")), tstr(v.get("hint"))))
|
||||
lines += [
|
||||
"};",
|
||||
f"static const int kOfferVerbCount = {len(A['verbs'])};",
|
||||
|
||||
@@ -67,6 +67,12 @@
|
||||
"angle": 225,
|
||||
"label": "Modify",
|
||||
"why": "edits or removes what is already there"
|
||||
},
|
||||
{
|
||||
"id": "top",
|
||||
"label": "Top",
|
||||
"flat": true,
|
||||
"why": "the verbs a selection is opened for most. A flat slot is not a family: its verbs sit first, at the top level of the offer, each an item of its own, and its label is never shown. Appended last, so no ratified index moved."
|
||||
}
|
||||
],
|
||||
"selections": [
|
||||
@@ -326,7 +332,7 @@
|
||||
"id": "rib",
|
||||
"name": "Rib",
|
||||
"slot": "add",
|
||||
"key": null,
|
||||
"key": "R",
|
||||
"feature": "Rib",
|
||||
"mcp": "rib",
|
||||
"accepts": [
|
||||
@@ -343,7 +349,7 @@
|
||||
},
|
||||
{
|
||||
"id": "boolean",
|
||||
"name": "Union",
|
||||
"name": "Join",
|
||||
"slot": "add",
|
||||
"key": "Shift+B",
|
||||
"feature": "Boolean",
|
||||
@@ -354,7 +360,7 @@
|
||||
"needs": {
|
||||
"bodies": 2
|
||||
},
|
||||
"refusal": "Boolean needs two bodies — create or import a second solid",
|
||||
"refusal": "Boolean needs two bodies — add or import a second one",
|
||||
"gui": true,
|
||||
"action": "btn:bool#0",
|
||||
"icon": "design_boolean",
|
||||
@@ -373,7 +379,7 @@
|
||||
"needs": {
|
||||
"bodies": 2
|
||||
},
|
||||
"refusal": "Boolean needs two bodies — create or import a second solid",
|
||||
"refusal": "Boolean needs two bodies — add or import a second one",
|
||||
"gui": true,
|
||||
"action": "btn:bool#1",
|
||||
"mode": null,
|
||||
@@ -393,7 +399,7 @@
|
||||
"needs": {
|
||||
"bodies": 2
|
||||
},
|
||||
"refusal": "Boolean needs two bodies — create or import a second solid",
|
||||
"refusal": "Boolean needs two bodies — add or import a second one",
|
||||
"gui": true,
|
||||
"action": "btn:bool#2",
|
||||
"mode": null,
|
||||
@@ -483,7 +489,7 @@
|
||||
"needs": {
|
||||
"sheet": true
|
||||
},
|
||||
"refusal": "target is not a sheet body",
|
||||
"refusal": "Thicken Surface needs a surface body — make one with a Surface tool first",
|
||||
"gui": true,
|
||||
"action": "fly:surface#5",
|
||||
"icon": "design_thicken",
|
||||
@@ -507,7 +513,7 @@
|
||||
"gui": true,
|
||||
"action": "key:S+H",
|
||||
"icon": "design_hole",
|
||||
"hint": "Drill a hole, centred on a picked face or placed on a plane"
|
||||
"hint": "Drill a hole, centerd on a picked face or placed on a plane"
|
||||
},
|
||||
{
|
||||
"id": "thread",
|
||||
@@ -543,7 +549,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Shell needs a solid body",
|
||||
"refusal": "Shell needs a solid body — add or import one first",
|
||||
"gui": true,
|
||||
"action": "key:S+K",
|
||||
"icon": "design_shell",
|
||||
@@ -569,7 +575,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Create a solid body to cut first",
|
||||
"refusal": "Cut needs a solid body — add or import one first",
|
||||
"gui": true,
|
||||
"action": "key:S+X",
|
||||
"icon": "design_cut",
|
||||
@@ -594,7 +600,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Split needs a solid body",
|
||||
"refusal": "Split needs a solid body — add or import one first",
|
||||
"gui": false,
|
||||
"action": null,
|
||||
"icon": null,
|
||||
@@ -677,7 +683,7 @@
|
||||
"needs": {
|
||||
"sheet": true
|
||||
},
|
||||
"refusal": "target is not a sheet body",
|
||||
"refusal": "Surface Offset needs a surface body — make one with a Surface tool first",
|
||||
"gui": true,
|
||||
"action": "fly:surface#4",
|
||||
"icon": "design_offset",
|
||||
@@ -699,7 +705,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Create a solid body to pattern first",
|
||||
"refusal": "Pattern needs a solid body — add or import one first",
|
||||
"gui": true,
|
||||
"action": "btn:pat#0",
|
||||
"icon": "design_array",
|
||||
@@ -721,7 +727,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Create a solid body to pattern first",
|
||||
"refusal": "Pattern needs a solid body — add or import one first",
|
||||
"gui": true,
|
||||
"action": "btn:pat#1",
|
||||
"mode": null,
|
||||
@@ -768,7 +774,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Pattern on curve needs a body and a curve",
|
||||
"refusal": "Pattern on Curve needs a body and a curve",
|
||||
"gui": false,
|
||||
"action": null,
|
||||
"icon": null,
|
||||
@@ -816,11 +822,11 @@
|
||||
"needs": {
|
||||
"bodies": 2
|
||||
},
|
||||
"refusal": "A mate needs two coordinate systems",
|
||||
"refusal": "Mate needs two coordinate systems — create them first",
|
||||
"gui": true,
|
||||
"action": "fly:placement#2",
|
||||
"icon": "design_c_coincident",
|
||||
"hint": "Assembly: align two CoordSys features (fastened, planar, revolute, slider, cylindrical)"
|
||||
"hint": "Assembly: align two coordinate systems (fastened, planar, revolute, slider, cylindrical)"
|
||||
},
|
||||
{
|
||||
"id": "align",
|
||||
@@ -835,7 +841,7 @@
|
||||
"needs": {
|
||||
"bodies": 1
|
||||
},
|
||||
"refusal": "Align needs a body",
|
||||
"refusal": "Align needs a body — add or import one first",
|
||||
"gui": false,
|
||||
"action": null,
|
||||
"icon": null,
|
||||
@@ -885,7 +891,7 @@
|
||||
},
|
||||
{
|
||||
"id": "coordsys_v",
|
||||
"name": "Coord Sys",
|
||||
"name": "Coordinate system",
|
||||
"slot": "reference",
|
||||
"key": "Shift+C",
|
||||
"feature": "CoordSys",
|
||||
@@ -972,7 +978,7 @@
|
||||
},
|
||||
{
|
||||
"id": "mass_props",
|
||||
"name": "Mass",
|
||||
"name": "Volume and area",
|
||||
"slot": "reference",
|
||||
"key": null,
|
||||
"feature": null,
|
||||
@@ -1003,14 +1009,15 @@
|
||||
"feature": null,
|
||||
"mcp": "check_interference",
|
||||
"accepts": [
|
||||
"body_solid",
|
||||
"bodies_2"
|
||||
],
|
||||
"needs": {
|
||||
"bodies": 2
|
||||
},
|
||||
"refusal": null,
|
||||
"gui": false,
|
||||
"action": null,
|
||||
"refusal": "Interference needs at least two bodies",
|
||||
"gui": true,
|
||||
"action": "btn:interference",
|
||||
"icon": null,
|
||||
"hint": "Check whether two bodies overlap — reports, changes nothing"
|
||||
},
|
||||
@@ -1043,7 +1050,7 @@
|
||||
{
|
||||
"id": "rename",
|
||||
"name": "Rename…",
|
||||
"slot": "modify",
|
||||
"slot": "top",
|
||||
"key": "F2",
|
||||
"feature": "Tree",
|
||||
"mcp": null,
|
||||
@@ -1082,8 +1089,8 @@
|
||||
},
|
||||
{
|
||||
"id": "colour",
|
||||
"name": "Colour",
|
||||
"slot": "modify",
|
||||
"name": "Color",
|
||||
"slot": "top",
|
||||
"key": null,
|
||||
"feature": null,
|
||||
"mcp": null,
|
||||
@@ -1104,7 +1111,7 @@
|
||||
"gui": true,
|
||||
"action": "btn:colour",
|
||||
"icon": "color_palette",
|
||||
"hint": "Set the selected body's display colour"
|
||||
"hint": "Set the selected body's display color"
|
||||
},
|
||||
{
|
||||
"id": "delete",
|
||||
@@ -1227,7 +1234,7 @@
|
||||
},
|
||||
{
|
||||
"id": "sk_rect_center",
|
||||
"name": "Centre rectangle",
|
||||
"name": "Center rectangle",
|
||||
"slot": "create",
|
||||
"key": null,
|
||||
"feature": "Sketch",
|
||||
@@ -1296,7 +1303,7 @@
|
||||
},
|
||||
{
|
||||
"id": "sk_circle",
|
||||
"name": "Centre circle",
|
||||
"name": "Center circle",
|
||||
"slot": "create",
|
||||
"key": "C",
|
||||
"feature": "Sketch",
|
||||
@@ -1411,7 +1418,7 @@
|
||||
},
|
||||
{
|
||||
"id": "sk_arc_center",
|
||||
"name": "Centre-point arc",
|
||||
"name": "Center-point arc",
|
||||
"slot": "create",
|
||||
"key": null,
|
||||
"feature": "Sketch",
|
||||
@@ -1453,7 +1460,7 @@
|
||||
"action": "key:S",
|
||||
"family": "Slot",
|
||||
"icon": "design_slot",
|
||||
"hint": "Slot — two centerline ends, then end radius"
|
||||
"hint": "Slot — two centerline ends, then the width"
|
||||
},
|
||||
{
|
||||
"id": "sk_slot_arc",
|
||||
@@ -1567,7 +1574,7 @@
|
||||
"mode": "sketch",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Triangle — click centre, then a vertex"
|
||||
"hint": "Triangle — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_poly_4",
|
||||
@@ -1590,7 +1597,7 @@
|
||||
"mode": "sketch",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Square — click centre, then a vertex"
|
||||
"hint": "Square — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_poly_5",
|
||||
@@ -1613,7 +1620,7 @@
|
||||
"mode": "sketch",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Pentagon — click centre, then a vertex"
|
||||
"hint": "Pentagon — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_polygon",
|
||||
@@ -1636,7 +1643,7 @@
|
||||
"action": "btn:poly#6",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Hexagon — click centre, then a vertex"
|
||||
"hint": "Hexagon — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_poly_8",
|
||||
@@ -1659,7 +1666,7 @@
|
||||
"mode": "sketch",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Octagon — click centre, then a vertex"
|
||||
"hint": "Octagon — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_poly_12",
|
||||
@@ -1682,7 +1689,7 @@
|
||||
"mode": "sketch",
|
||||
"family": "Polygon",
|
||||
"icon": "design_polygon",
|
||||
"hint": "Dodecagon — click centre, then a vertex"
|
||||
"hint": "Dodecagon — click center, then a vertex"
|
||||
},
|
||||
{
|
||||
"id": "sk_poly_inscribed",
|
||||
@@ -1772,7 +1779,7 @@
|
||||
"action": "btn:text",
|
||||
"mode": "sketch",
|
||||
"icon": "design_text",
|
||||
"hint": "Type text; its outline is added to this sketch as editable lines"
|
||||
"hint": "Type text; it becomes a Text feature on this sketch's plane, editable later"
|
||||
},
|
||||
{
|
||||
"id": "sk_svg",
|
||||
@@ -2048,7 +2055,7 @@
|
||||
{
|
||||
"id": "constrain",
|
||||
"name": "Constrain sketch",
|
||||
"slot": "modify",
|
||||
"slot": "reference",
|
||||
"key": null,
|
||||
"feature": "Sketch",
|
||||
"mcp": null,
|
||||
@@ -2067,7 +2074,8 @@
|
||||
"note": [
|
||||
"Same verb, model-mode vocabulary: offered when a SKETCH is selected (bit 14, SkLoop), the",
|
||||
"state a user is in right after finishing one. Without this row the only way in was the",
|
||||
"toolbar icon, and constraints read as absent — see the Onshape-comparison report."
|
||||
"toolbar icon, and constraints read as absent — see the Onshape-comparison report.",
|
||||
"It sits in the Reference row, the address the sketch-mode Constrain has: one verb, one row."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ struct CadFeature {
|
||||
// the OCCT shape verbatim — it is adopted as a base body in route_feature (no parametric
|
||||
// recipe). Downstream face/edge features (fillet/chamfer/cut/shell/...) act on it like any
|
||||
// other body. TopoDS_Shape is a cheap handle, so copying it through recompute/checkpoint
|
||||
// snapshots is cheap. In-session only for now (no BRep serialization yet).
|
||||
// snapshots is cheap. Saved with the recipe as a BRep string (see save()/load()).
|
||||
TopoDS_Shape imported_solid;
|
||||
|
||||
// Non-destructive placement transform for imported_regions (Text/SVG),
|
||||
@@ -116,6 +116,25 @@ struct CadFeature {
|
||||
double dressup_size{1.0}; // fillet radius or chamfer distance
|
||||
FaceGroup face_group{FaceGroup::All};
|
||||
int dressup_edge{-1}; // global edge id for edge-targeted fillet/chamfer; -1 = use face_group
|
||||
// Several picked edges dressed in ONE operation, all resolved against the same body, so
|
||||
// the ids cannot drift the way they do across a chain of single-edge features. When set,
|
||||
// dressup_edge holds the first of them: a build that predates the list still dresses that
|
||||
// one edge instead of falling back to the whole face group.
|
||||
std::vector<int> dressup_edges;
|
||||
// The edges this dress-up targets: the list, else the single edge, else none (face group).
|
||||
// Text feature: a Sketch whose imported_regions were vectorised from this string in this font
|
||||
// (a WxFontUtils descriptor, bold/italic included) at this cap height in mm. The regions are
|
||||
// what gets built — they are saved too, so the project opens on a machine without the font —
|
||||
// and these three are what an edit reopens the Text dialog with. Empty = not a text feature.
|
||||
std::string text_string;
|
||||
std::string text_font;
|
||||
double text_height{0.0};
|
||||
bool is_text() const { return !text_string.empty(); }
|
||||
std::vector<int> dressup_edge_ids() const {
|
||||
if (!dressup_edges.empty()) return dressup_edges;
|
||||
if (dressup_edge >= 0) return { dressup_edge };
|
||||
return {};
|
||||
}
|
||||
|
||||
// Hole params (positioned circular cut into the current body)
|
||||
double hole_diameter{5};
|
||||
@@ -134,12 +153,21 @@ struct CadFeature {
|
||||
std::string hole_standard; // provenance only, e.g. "M6" / "1/4-20"; not used by geometry
|
||||
|
||||
// Thread params (helical thread about the plane normal at a positioned point)
|
||||
double thread_radius{5}; // nominal cylinder radius
|
||||
double thread_radius{5}; // MAJOR (nominal) radius when thread_major_nominal,
|
||||
// else the legacy reference radius (see below)
|
||||
double thread_pitch{2}; // axial advance per turn
|
||||
double thread_height{10}; // total axial length
|
||||
double thread_depth{1}; // radial crest depth of the thread profile
|
||||
bool thread_internal{false}; // false = external threaded rod (New body);
|
||||
// true = tapped bore cut into the current body
|
||||
double thread_depth{1}; // radial depth of the thread profile
|
||||
bool thread_internal{false}; // false = external: the thread goes on the target body
|
||||
// (or on a standalone rod when there is none);
|
||||
// true = tapped bore cut into the target body
|
||||
// How thread_radius is read. true (every thread made since): it is the nominal MAJOR
|
||||
// radius, for both kinds — an internal thread bores to R - depth and grooves out to R, an
|
||||
// external one is a rod of radius R with its groove cut in to R - depth, so an M6 is 6 mm
|
||||
// across its crests either way. false (older recipes, kept bit-for-bit): the ridge was
|
||||
// added OUTSIDE R, so an internal thread given the minor diameter came out undersized and
|
||||
// an external one given the major diameter came out oversized.
|
||||
bool thread_major_nominal{false};
|
||||
double thread_x{0}; // axis position on the plane (u/x axis)
|
||||
double thread_y{0}; // axis position on the plane (v/y axis)
|
||||
|
||||
@@ -156,6 +184,9 @@ struct CadFeature {
|
||||
// target_body. revolve_axis: 0 = plane X axis, 1 = plane Y axis.
|
||||
double revolve_angle{360}; // sweep angle in degrees (1..360)
|
||||
int revolve_axis{0}; // 0 = plane X, 1 = plane Y
|
||||
// A Line of the profile sketch to revolve about instead (index into its entities: a
|
||||
// centerline, usually construction, or an edge of the profile itself); -1 = revolve_axis.
|
||||
int revolve_axis_entity{-1};
|
||||
|
||||
// Sweep: profile carried by sketch_ref / entities (like Extrude); the spine is a
|
||||
// second Sketch referenced by sweep_path_ref (an open or closed wire). Reuses
|
||||
@@ -178,6 +209,12 @@ struct CadFeature {
|
||||
double pattern_spacing{20}; // linear step (mm)
|
||||
int pattern_dir{0}; // linear direction: 0 = plane X, 1 = plane Y
|
||||
double pattern_angle{360}; // circular total angle (degrees)
|
||||
// How a circular pattern spreads its copies over pattern_angle. true (every pattern made
|
||||
// since): a full turn is split into `count` equal steps, anything less is spanned end to end,
|
||||
// first copy at 0 and last at pattern_angle — the way a pattern along a curve spans its
|
||||
// curve. false (older recipes, kept as they were built): always angle / count, so 90° with 3
|
||||
// copies stopped at 60°.
|
||||
bool pattern_inclusive{false};
|
||||
|
||||
// Pattern along a curve: when pattern_curve_sketch >= 0 this mode takes precedence over
|
||||
// linear/circular. Copies are placed at equal-parameter points along entity
|
||||
@@ -190,6 +227,16 @@ struct CadFeature {
|
||||
// BEFORE geometry runs. Empty (the common case) means the feature uses its literal fields.
|
||||
std::map<std::string, std::string> expr;
|
||||
|
||||
// Transient, never serialized. A body is referred to above by its INDEX, and an index is
|
||||
// only meaningful against the history it was taken in: delete, reorder or disable a
|
||||
// feature that makes a body and every later index points at a different body. So recompute
|
||||
// records, for each body field, the identity of the body it resolved to — the feature that
|
||||
// made it, and which of that feature's bodies it is — and when the history changes the
|
||||
// fields are re-pointed from those identities (see CadDocument::recompute).
|
||||
struct BodyId { int src{-1}; int ord{0}; };
|
||||
std::vector<BodyId> body_ref_ids;
|
||||
bool body_refs_pending{false};
|
||||
|
||||
// Datum/reference plane: a derived SketchPlane the document offers as a selectable
|
||||
// sketch plane (no solid). plane_base selects the reference (0=XY,1=XZ,2=YZ, or 3+N
|
||||
// = the Nth earlier datum plane); plane_offset shifts along the base normal;
|
||||
@@ -361,10 +408,62 @@ struct CadFeature {
|
||||
pattern_curve_sketch, pattern_curve_entity,
|
||||
expr,
|
||||
mate_kind, mate_cs_a, mate_cs_b, mate_offset, mate_angle, mate_flip,
|
||||
coordsys_face_kind, coordsys_face_edges);
|
||||
coordsys_face_kind, coordsys_face_edges,
|
||||
thread_major_nominal, pattern_inclusive,
|
||||
dressup_edges,
|
||||
text_string, text_font, text_height,
|
||||
revolve_axis_entity);
|
||||
}
|
||||
template<class Archive>
|
||||
void load(Archive& ar) {
|
||||
std::string brep;
|
||||
ar(type, name, enabled, shape, plane, width, height, radius,
|
||||
profile, entities, constraints, entity_constraints, imported_regions,
|
||||
import_offset, import_scale_x, import_scale_y, import_on_face, import_face_body,
|
||||
sketch_ref, distance, symmetric, mode, extrude_end, distance2, taper_deg, flip,
|
||||
up_to_face, extrude_src_face, up_to_point, target_body,
|
||||
dressup_size, face_group, dressup_edge,
|
||||
hole_diameter, hole_depth, hole_through, hole_x, hole_y,
|
||||
thread_radius, thread_pitch, thread_height, thread_depth, thread_internal, thread_x, thread_y,
|
||||
shell_thickness, shell_face,
|
||||
draft_face, draft_angle,
|
||||
revolve_angle, revolve_axis,
|
||||
sweep_path_ref, loft_profile_refs, loft_ruled,
|
||||
pattern_circular, pattern_count, pattern_spacing, pattern_dir, pattern_angle,
|
||||
plane_base, plane_offset, plane_angle_tilt, plane_axis,
|
||||
bool_tool_body, bool_keep_tool, bool_tolerance, bool_target_face, bool_tool_face,
|
||||
cut_offset, cut_flip, cut_keep_upper, cut_keep_lower,
|
||||
brep,
|
||||
plane_type, plane_face_body, plane_face, plane_face2_body, plane_face2,
|
||||
plane_edge_body, plane_edge, plane_edge2_body, plane_edge2, plane_u_size, plane_v_size,
|
||||
mirror_keep_original,
|
||||
axis_type, axis_p1, axis_p2, axis_body, axis_face, axis_edge, axis_plane_a, axis_plane_b,
|
||||
coordsys_type, coordsys_point, coordsys_body, coordsys_face, coordsys_edge, coordsys_x_hint,
|
||||
helix_radius, helix_pitch, helix_height, helix_left_handed, helix_taper_deg,
|
||||
xf_translate, xf_axis, xf_pivot, xf_angle_deg, xf_copy,
|
||||
thicken_face, thicken_thickness, thicken_flip,
|
||||
cut_face_body, cut_face,
|
||||
project_source_body, project_edges, project_face,
|
||||
delete_faces,
|
||||
hole_style, hole_cbore_diameter, hole_cbore_depth,
|
||||
hole_csink_diameter, hole_csink_angle, hole_standard,
|
||||
rib_sketch_ref, rib_entity, rib_thickness, rib_depth,
|
||||
pattern_curve_sketch, pattern_curve_entity,
|
||||
expr,
|
||||
mate_kind, mate_cs_a, mate_cs_b, mate_offset, mate_angle, mate_flip,
|
||||
coordsys_face_kind, coordsys_face_edges,
|
||||
thread_major_nominal, pattern_inclusive,
|
||||
dressup_edges,
|
||||
text_string, text_font, text_height,
|
||||
revolve_axis_entity);
|
||||
imported_solid = brep_from_string(brep);
|
||||
}
|
||||
// The pre-framing (v4) layout, FROZEN. A v4 recipe is one flat stream with no per-feature
|
||||
// length, so it can only be read with exactly the field list it was written with; reading it
|
||||
// through load() above breaks the moment a field is appended there (every field added since
|
||||
// v5 is appended ONLY above, never here).
|
||||
template<class Archive>
|
||||
void load_flat_v4(Archive& ar) {
|
||||
std::string brep;
|
||||
ar(type, name, enabled, shape, plane, width, height, radius,
|
||||
profile, entities, constraints, entity_constraints, imported_regions,
|
||||
@@ -403,6 +502,7 @@ struct CadFeature {
|
||||
coordsys_face_kind, coordsys_face_edges);
|
||||
imported_solid = brep_from_string(brep);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Serialize a TopoDS_Shape to/from a BRep string for cereal persistence.
|
||||
@@ -440,6 +540,13 @@ public:
|
||||
// Named document variables: name -> expression. Evaluated topologically each recompute();
|
||||
// an expression may reference other variables. Feature `expr` bindings resolve against these.
|
||||
std::map<std::string, std::string> variables;
|
||||
// Can a feature expression drive this numeric field? The single allow-list the evaluator
|
||||
// uses, so a caller can refuse a name before it breaks the next recompute.
|
||||
static bool is_bindable_field(const std::string& field);
|
||||
// Does a feature of this type leave a body behind? Sketches, datums, the helix curve and
|
||||
// Project (which emits sketch entities) do not. The one answer recompute(), the rollback
|
||||
// rule and the GUI's preview all use.
|
||||
static bool produces_body(CadFeatureType t);
|
||||
// Multi-body result of the last replay. A "New" extrude appends a body; other ops
|
||||
// mutate a target body. Empty after a failed/empty recompute.
|
||||
std::vector<CadBody> bodies;
|
||||
@@ -459,8 +566,16 @@ public:
|
||||
|
||||
// Modeling origin: the world point the default XY/XZ/YZ planes pass through. The GUI sets this
|
||||
// to the bed centre so sketches/datums land in the middle of the bed (not the bed corner =
|
||||
// world 0). Not serialized — the GUI re-applies it from the live bed on every tab show.
|
||||
// world 0) — for a NEW document. It is saved with the recipe: sketches bake it into their
|
||||
// planes while datum planes add it when they are resolved, so a project reopened on another
|
||||
// printer must keep the origin it was made with or its datums move and its sketches do not.
|
||||
Vec3d modeling_origin{Vec3d::Zero()};
|
||||
bool origin_from_recipe{false}; // modeling_origin came from the loaded project
|
||||
// Weld sketch endpoints within kSketchJoinTol (the "Auto-close sketch loops" preference, taken
|
||||
// when the document is started). A property of the DOCUMENT, saved with it: as a machine-wide
|
||||
// preference it made one project rebuild into a closed solid on one computer and an open
|
||||
// loop on another. recompute() pushes it into the kernel.
|
||||
bool auto_close_loops{true};
|
||||
|
||||
// Tessellation quality, matched to Orca's OWN STEP importer (Format/STEP.hpp defaults:
|
||||
// linear 0.003, angular 0.5 rad) so a body modelled here reaches the screen at the same
|
||||
@@ -513,8 +628,10 @@ public:
|
||||
BooleanMode mode, const std::string& name);
|
||||
int add_fillet(double radius, FaceGroup faces, const std::string& name);
|
||||
int add_fillet(double radius, int edge_id, const std::string& name);
|
||||
int add_fillet(double radius, const std::vector<int>& edge_ids, const std::string& name);
|
||||
int add_chamfer(double distance, FaceGroup faces, const std::string& name);
|
||||
int add_chamfer(double distance, int edge_id, const std::string& name);
|
||||
int add_chamfer(double distance, const std::vector<int>& edge_ids, const std::string& name);
|
||||
int add_hole(double diameter, double depth, bool through,
|
||||
double x, double y, const SketchPlane& plane,
|
||||
const std::string& name);
|
||||
@@ -707,6 +824,9 @@ public:
|
||||
// an Extrude, its sketch_ref are preserved from the original).
|
||||
bool remove_feature(int index);
|
||||
bool move_feature(int index, int delta);
|
||||
// Show or hide a feature. Transactional like the others, and it keeps body and datum-plane
|
||||
// references pointing at the same objects, which a bare `enabled` flip does not.
|
||||
bool set_feature_enabled(int index, bool enabled);
|
||||
bool replace_feature(int index, const CadFeature& edited);
|
||||
// replace_sketch_extrude: a box is two linked features (Sketch + Extrude);
|
||||
// overwrite both slots from one `edited` candidate (sketch params ->
|
||||
@@ -718,6 +838,9 @@ public:
|
||||
// tessellate the result into out_mesh, WITHOUT modifying features/body/
|
||||
// display_mesh. Returns false (with err set) if the candidate is invalid.
|
||||
// Used by the Design tab to show a translucent ghost before Confirm.
|
||||
// The solid body the closed profile of sketch `sketch_ref` lies on or touches, or -1. Drives
|
||||
// the Extrude default: a profile drawn on a body joins it, one in free space is a new body.
|
||||
int body_touching_sketch(int sketch_ref) const;
|
||||
bool preview(const CadFeature& candidate, TriangleMesh& out_mesh, std::string& err) const;
|
||||
// Same, but also returns the per-body meshes (in `bodies` order; the candidate may append
|
||||
// one), so the GUI can apply its display-only per-body Move transforms to the ghost and keep
|
||||
@@ -725,6 +848,16 @@ public:
|
||||
bool preview(const CadFeature& candidate, TriangleMesh& out_mesh,
|
||||
std::vector<TriangleMesh>& out_body_meshes, std::string& err) const;
|
||||
|
||||
// The faces of the current bodies that features[index] made, as (body, face id) pairs: what
|
||||
// the Design tab highlights when that feature is selected. A face counts when it lies on the
|
||||
// model's boundary, facing the same way, right after the feature and not right before it, so
|
||||
// a face a later feature trimmed still belongs to the one that made it. Positions are
|
||||
// compared, no history is kept: a face a later feature rebuilt in place stays the earlier
|
||||
// feature's. A feature with no face left of its own (a Boolean union, or one whose faces a
|
||||
// later feature removed) answers with every face of each body it changed. Empty for a feature
|
||||
// that leaves no body, a hidden one, or a history that no longer rebuilds up to it.
|
||||
std::vector<std::pair<int, int>> faces_made_by(int index) const;
|
||||
|
||||
private:
|
||||
TopoDS_Wire build_sketch_wire(const CadFeature& sketch, bool closed_only = false) const;
|
||||
// The planar region an Extrude sweeps: the sketch's outer loop with its inner loops as
|
||||
@@ -741,6 +874,13 @@ private:
|
||||
// starts a new body (empty list, or an Extrude with mode New) vs mutates an existing
|
||||
// one, then apply_feature. Shared by recompute() (replay all) and preview() (candidate).
|
||||
void route_feature(std::vector<CadBody>& bodies, const CadFeature& f) const;
|
||||
// The parametric pass every replay starts with: evaluate the variables and write each
|
||||
// feature's expression bindings into its numeric fields.
|
||||
void bind_expressions();
|
||||
// One step of the replay: route features[fi] into `built` and stamp the bodies it created
|
||||
// with fi. A hidden feature, a sketch, a helix or a datum leaves `built` alone. Throws on
|
||||
// failure.
|
||||
void replay_feature(size_t fi, std::vector<CadBody>& built);
|
||||
// Boolean between two existing bodies: resolve target + tool, optionally snap the tool so
|
||||
// the picked faces mate, run the OCCT op (with fuzzy tolerance), write the result back to the
|
||||
// target and erase the consumed tool. Mutates the bodies vector directly (unlike apply_feature,
|
||||
|
||||
@@ -294,21 +294,21 @@ FaceGroup GeometryEngine::classify_face(const TopoDS_Face& face, const TopoDS_Sh
|
||||
|
||||
std::vector<TopoDS_Edge> GeometryEngine::collect_edges(const TopoDS_Shape& solid, FaceGroup target)
|
||||
{
|
||||
// Each edge ONCE. An explorer visits a shared edge from both of its faces, so walking one
|
||||
// handed every edge to the fillet twice; the de-duplicated map is also what edge_by_index
|
||||
// numbers edges by.
|
||||
std::vector<TopoDS_Edge> result;
|
||||
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> edgeFaceMap;
|
||||
TopExp::MapShapesAndAncestors(solid, TopAbs_EDGE, TopAbs_FACE, edgeFaceMap);
|
||||
if (target == FaceGroup::All) {
|
||||
for (TopExp_Explorer exp(solid, TopAbs_EDGE); exp.More(); exp.Next())
|
||||
result.push_back(TopoDS::Edge(exp.Current()));
|
||||
for (int i = 1; i <= edgeFaceMap.Extent(); ++i)
|
||||
result.push_back(TopoDS::Edge(edgeFaceMap.FindKey(i)));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build edge-to-face map once
|
||||
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> edgeFaceMap;
|
||||
TopExp::MapShapesAndAncestors(solid, TopAbs_EDGE, TopAbs_FACE, edgeFaceMap);
|
||||
|
||||
for (TopExp_Explorer edgeExp(solid, TopAbs_EDGE); edgeExp.More(); edgeExp.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(edgeExp.Current());
|
||||
if (!edgeFaceMap.Contains(edge)) continue;
|
||||
const NCollection_List<TopoDS_Shape>& faces = edgeFaceMap.FindFromKey(edge);
|
||||
for (int ei = 1; ei <= edgeFaceMap.Extent(); ++ei) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(edgeFaceMap.FindKey(ei));
|
||||
const NCollection_List<TopoDS_Shape>& faces = edgeFaceMap.FindFromIndex(ei);
|
||||
|
||||
bool include = false;
|
||||
for (auto it = faces.begin(); it != faces.end(); ++it) {
|
||||
@@ -344,12 +344,14 @@ std::vector<TopoDS_Edge> GeometryEngine::collect_edges(const TopoDS_Shape& solid
|
||||
|
||||
// ---- Fillet/Chamfer ----
|
||||
|
||||
// All four dress-up entry points fail the same way — with a reason — instead of two of them
|
||||
// handing the solid back unchanged, which recompute then reported as a success.
|
||||
TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radius, FaceGroup faces)
|
||||
{
|
||||
if (radius <= 0.001) return solid;
|
||||
if (radius <= 0.001) throw std::runtime_error("the fillet radius must be greater than 0");
|
||||
|
||||
std::vector<TopoDS_Edge> edges = collect_edges(solid, faces);
|
||||
if (edges.empty()) return solid;
|
||||
if (edges.empty()) throw std::runtime_error("no edges to fillet in that group");
|
||||
|
||||
BRepFilletAPI_MakeFillet fillet(solid);
|
||||
for (const auto& edge : edges)
|
||||
@@ -365,10 +367,10 @@ TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radi
|
||||
|
||||
TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double distance, FaceGroup faces)
|
||||
{
|
||||
if (distance <= 0.001) return solid;
|
||||
if (distance <= 0.001) throw std::runtime_error("the chamfer distance must be greater than 0");
|
||||
|
||||
std::vector<TopoDS_Edge> edges = collect_edges(solid, faces);
|
||||
if (edges.empty()) return solid;
|
||||
if (edges.empty()) throw std::runtime_error("no edges to chamfer in that group");
|
||||
|
||||
BRepFilletAPI_MakeChamfer chamfer(solid);
|
||||
for (const auto& edge : edges)
|
||||
@@ -381,28 +383,42 @@ TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double dis
|
||||
|
||||
TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radius, int edge_id)
|
||||
{
|
||||
if (radius <= 0.001) return solid;
|
||||
return apply_fillet(solid, radius, std::vector<int>{ edge_id });
|
||||
}
|
||||
|
||||
TopoDS_Edge edge = edge_by_index(solid, edge_id);
|
||||
if (edge.IsNull()) throw std::runtime_error("apply_fillet: invalid edge id");
|
||||
TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double distance, int edge_id)
|
||||
{
|
||||
return apply_chamfer(solid, distance, std::vector<int>{ edge_id });
|
||||
}
|
||||
|
||||
TopoDS_Shape GeometryEngine::apply_fillet(const TopoDS_Shape& solid, double radius, const std::vector<int>& edge_ids)
|
||||
{
|
||||
if (radius <= 0.001) throw std::runtime_error("the fillet radius must be greater than 0");
|
||||
if (edge_ids.empty()) throw std::runtime_error("apply_fillet: no edge picked");
|
||||
|
||||
BRepFilletAPI_MakeFillet mk(solid);
|
||||
mk.Add(radius, edge);
|
||||
for (int id : edge_ids) {
|
||||
TopoDS_Edge edge = edge_by_index(solid, id);
|
||||
if (edge.IsNull()) throw std::runtime_error("apply_fillet: invalid edge id");
|
||||
mk.Add(radius, edge);
|
||||
}
|
||||
mk.Build();
|
||||
|
||||
if (!mk.IsDone()) throw std::runtime_error("apply_fillet: OCCT fillet failed");
|
||||
return mk.Shape();
|
||||
}
|
||||
|
||||
TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double distance, int edge_id)
|
||||
TopoDS_Shape GeometryEngine::apply_chamfer(const TopoDS_Shape& solid, double distance, const std::vector<int>& edge_ids)
|
||||
{
|
||||
if (distance <= 0.001) return solid;
|
||||
|
||||
TopoDS_Edge edge = edge_by_index(solid, edge_id);
|
||||
if (edge.IsNull()) throw std::runtime_error("apply_chamfer: invalid edge id");
|
||||
if (distance <= 0.001) throw std::runtime_error("the chamfer distance must be greater than 0");
|
||||
if (edge_ids.empty()) throw std::runtime_error("apply_chamfer: no edge picked");
|
||||
|
||||
BRepFilletAPI_MakeChamfer mk(solid);
|
||||
mk.Add(distance, edge);
|
||||
for (int id : edge_ids) {
|
||||
TopoDS_Edge edge = edge_by_index(solid, id);
|
||||
if (edge.IsNull()) throw std::runtime_error("apply_chamfer: invalid edge id");
|
||||
mk.Add(distance, edge);
|
||||
}
|
||||
mk.Build();
|
||||
|
||||
if (!mk.IsDone()) throw std::runtime_error("apply_chamfer: OCCT chamfer failed");
|
||||
@@ -614,6 +630,27 @@ std::vector<Vec3d> GeometryEngine::sample_edge_world(const TopoDS_Edge& edge, do
|
||||
return pts;
|
||||
}
|
||||
|
||||
std::vector<std::vector<Vec3d>> GeometryEngine::display_edges(const TopoDS_Shape& shape, double chord_tol)
|
||||
{
|
||||
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> faces_of_edge;
|
||||
TopExp::MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, faces_of_edge);
|
||||
std::vector<std::vector<Vec3d>> out;
|
||||
for (int i = 1; i <= faces_of_edge.Extent(); ++i) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(faces_of_edge.FindKey(i));
|
||||
if (BRep_Tool::Degenerated(edge))
|
||||
continue;
|
||||
bool seam = false;
|
||||
for (NCollection_List<TopoDS_Shape>::Iterator it(faces_of_edge.FindFromIndex(i)); it.More() && !seam; it.Next())
|
||||
seam = BRep_Tool::IsClosed(edge, TopoDS::Face(it.Value()));
|
||||
if (seam)
|
||||
continue;
|
||||
std::vector<Vec3d> pts = sample_edge_world(edge, chord_tol);
|
||||
if (pts.size() >= 2)
|
||||
out.push_back(std::move(pts));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Vec3d GeometryEngine::face_centroid_world(const TopoDS_Face& face)
|
||||
{
|
||||
GProp_GProps props;
|
||||
|
||||
@@ -39,8 +39,9 @@ struct PrimitiveParams {
|
||||
double dressup_radius{1.0}; // fillet radius
|
||||
double dressup_chamfer_dist{1.0}; // chamfer distance (symmetric)
|
||||
|
||||
// Mesh quality
|
||||
double linear_deflection{0.01};
|
||||
// Mesh quality — the Design tab's own density (CadDocument::linear_deflection), so a
|
||||
// primitive and the same body modelled in the Design tab reach the screen alike.
|
||||
double linear_deflection{0.003};
|
||||
double angular_deflection{0.5};
|
||||
|
||||
template<class Archive>
|
||||
@@ -122,9 +123,14 @@ public:
|
||||
FaceGroup faces = FaceGroup::All);
|
||||
static TopoDS_Shape apply_chamfer(const TopoDS_Shape& solid, double distance,
|
||||
int edge_id);
|
||||
// Several edges in one operation, all ids resolved against `solid`.
|
||||
static TopoDS_Shape apply_fillet(const TopoDS_Shape& solid, double radius,
|
||||
const std::vector<int>& edge_ids);
|
||||
static TopoDS_Shape apply_chamfer(const TopoDS_Shape& solid, double distance,
|
||||
const std::vector<int>& edge_ids);
|
||||
|
||||
static TriangleMesh tessellate(const TopoDS_Shape& shape,
|
||||
double linear_deflection = 0.01,
|
||||
double linear_deflection = 0.003,
|
||||
double angular_deflection = 0.5);
|
||||
static std::string primitive_name(PrimitiveType type);
|
||||
|
||||
@@ -146,6 +152,10 @@ public:
|
||||
static Vec3d face_normal_world(const TopoDS_Face& face);
|
||||
// Sample an edge into a world-space polyline (>=2 pts) for pick-distance + highlight.
|
||||
static std::vector<Vec3d> sample_edge_world(const TopoDS_Edge& edge, double chord_tol = 0.05);
|
||||
// The edges a viewer draws over a body, each as a polyline: every edge of the shape once,
|
||||
// without degenerate edges (a cone apex) and without the seam of a closed surface (the line
|
||||
// down a cylinder's side), which is where OCCT closes the parameter space, not a real edge.
|
||||
static std::vector<std::vector<Vec3d>> display_edges(const TopoDS_Shape& shape, double chord_tol);
|
||||
// 0-based edge index into TopExp::MapShapes(shape, TopAbs_EDGE, map).
|
||||
static int edge_count(const TopoDS_Shape& shape);
|
||||
static TopoDS_Edge edge_by_index(const TopoDS_Shape& shape, int index);
|
||||
|
||||