Compare commits

..

120 Commits

Author SHA1 Message Date
dependabot[bot]
5bf43d92b9 chore(deps): bump actions/setup-python from 6 to 7
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 16:24:21 +00:00
Dipl.-Ing. Raoul Rubien, BSc
95b781745d Fixes 4 Compiler Warnings (#10727)
* fixes: may be used uninitialized [-Wmaybe-uninitialized]

* fixes: arc_len_next may be used uninitialized [-Wmaybe-uninitialized]

* review result: reverts {} initializer with = to keep code style consistent
2026-08-01 18:07:15 +08:00
Kenneth Raplee
80e64f80a6 Fix 32-bit build in LayerResult::make_nop_layer_result (#15036)
LayerResult's second field is typed size_t, so std::numeric_limits::max
should also use size_t and not something related to coordinates for the
layer_id.
2026-08-01 11:49:28 +08:00
yw4z
477208a969 Remove borders and paddings from native controls on Linux (#14873)
* init

* update

* Update SpinInput.cpp

* possible fix for em_unit

* button alignment

* match titlebar height

* revert em_value for macOS and Windows

* Update GUI_Utils.hpp

* Merge branch 'main' into linux-black-borders-2

* Revert "button alignment"

This reverts commit 3fc7461071.

* Revert "match titlebar height"

This reverts commit c4aa1d9f1e.

* revert dpi changes

* match platform tags

* remove radio box borders

* Fix code indent
2026-08-01 11:28:07 +08:00
Rodrigo Faselli
2e08b19d6b Outline MSAA (#14835)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-31 20:51:48 -03:00
SoftFever
ea7117b7ca Fix stringing between the model and the wipe tower (#15038)
# Description

On multi-tool printers using the type 2 wipe tower, the travel to the
tower ignored the configured Z hop type and always used a plain vertical
hop, so the nozzle rose in place over the part and oozed instead of
lifting away with the travel. It now follows the filament's Z hop
setting, matching what the type 1 tower already does.
Only toolchange travels to a type 2 tower change. Normal Lift and z_hop
= 0 are unaffected, and no extrusion moves change in any mode.

# Screenshots/Recordings/Graphs


## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-31 18:26:48 +08:00
SoftFever
603a8f9c8f Fix stringing between the model and the wipe tower
The tower travel took retract()'s default vertical Z hop instead of the
configured one, so the nozzle rose in place over the part and oozed
rather than departing with the travel. Pass the filament's z_hop_types
through, mapping Auto to a spiral lift as append_tcr does.
2026-07-31 18:16:06 +08:00
SoftFever
6a2a57bdf4 Bring the rib wall and skip-points prime tower to all printers (#15035)
* Sync WipeTower from BambuStudio(through ca1881761)

* Fix post-slice self-invalidation on custom multi-extruder printers

* Complete the rib wipe tower port in WipeTower2

The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.

* Use the squared rib tower size in arrange estimates

estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.

* Print the WipeTower2 shell with a non-support, non-soluble filament

Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.

* Port the skip-points gap wall to WipeTower2

prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.

* Route the in-place toolchange tower entry through the skip-point gap

On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).

* Iron the purge start out through the skip-point gap in WipeTower2

Port the BBL tower's entry line ironing: extrude the first 3 mm of the
purge, retract, drag the nozzle 1.5x back out through the wall gap at
F600, creep back at F240 and unretract, so the toolchange start blob
ends up in the gap instead of on the wall. Fires only when the purge
starts at the left-edge entry heading right (in-place toolchangers);
SEMM ram/cooling wipes start mid-box and the priming line has no wall,
so both keep their previous output.

* Reserve WipeTower2 toolchange depth to match the printed purge

The planner reserved ramming rows gated only on enable_filament_ramming and
sized them with the SEMM 0.25s time step, while toolchange_Unload rams on
(semm && enable_filament_ramming) || filament_multitool_ramming with the
multitool time step. Disabling multitool ramming therefore left ~3 unprinted
rows per toolchange as blank bands in the tower. Without ramming the first
wipe line also needs reserved depth of its own (it no longer rides the last
ramming row), plus the y_step/2 offset the wipe start inherits from the
ramming start position - otherwise the tightened boxes truncate the ordered
purge at the box edge.

* Tile WipeTower2 purge rows contiguously across toolchange blocks

Without ramming, each purge block reserved one wipe pitch more than its
rows occupy (ceil+1 rounding plus the ram-geometry start offset), and the
wipe began a full pitch inside the block, leaving a blank band of exactly
two pitches between adjacent blocks. Plan the block as whole wipe rows,
start the first row so the row lattice continues across the block
boundary, and fill the reserved box instead of stopping at the ordered
volume, mirroring how the BBL WipeTower keeps planned depth identical to
printed rows. Ram-printing toolchanges (SEMM with ramming enabled,
multitool ramming) are unchanged.

* Scrub the WipeTower2 toolchange entry with the BBL flat-ironing spiral

The entry scrub now matches the BBL tower's toolchange_wipe_new sequence:
after the ironing drag the retracted nozzle runs a dry expanding-square
spiral centred on the wall-gap entry point before resuming the purge row.
The spiral runs whenever the gap wall is on (disable per filament via
filament_tower_ironing_area = 0); WipeTower2 no longer reads
prime_tower_flat_ironing.

* Restart the WipeTower2 wipe at the box boundary after multitool ramming

With the gap wall on a multi-tool printer, quantize the ram band up to its
whole reserved rows (as the BBL tower does for the old-tool purge) and start
CP TOOLCHANGE WIPE at the left-edge boundary on a fresh row below it instead
of continuing from wherever the ram serpentine ended. The entry scrub then
runs at the wall gap on ram toolchanges too, and the wipe box is whole rows,
so it is filled completely like the no-ram case. SEMM and skip-points-off
behavior is unchanged.

* Move the WipeTower2 wall gap to the wipe start row for ram toolchanges

* code cleanup

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix typo

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-31 18:08:55 +08:00
SoftFever
c8aacea176 fix typo 2026-07-31 18:07:46 +08:00
SoftFever
4824a171f1 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-31 15:34:04 +08:00
Ian Chua
101f43b2d8 fix: resolve plugins that are missing locally (physical file deleted) (#14861)
* fix: resolve plugins that are missing locally (physical file deleted)

* fix: don't treat file not found as an error
2026-07-31 14:15:51 +08:00
Ian Chua
e01ac1f0a6 fix: keep orphaned cloud plugins runnable (#14859)
* fix: keep orphaned cloud plugins runnable

* fix: tests
2026-07-31 14:15:22 +08:00
Ian Chua
b51db32bb5 fix: ota profile race (#14993)
# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
This PR updates OTA vendor profile handling and fixes a Windows-only
startup race that could terminate OrcaSlicer silently.

## Changes
- Correct vendor profile version comparison logic.
- Trigger vendor profile synchronization after the startup printer
preset is restored.
- Remove duplicate vendor synchronization from the general startup
updater.
- Replace global temporary archive cleanup with targeted per-vendor
cleanup.
- Add updater-thread exception handling to prevent uncaught filesystem
errors from terminating the process.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-31 14:14:34 +08:00
Ian Chua
603b02f0e7 Merge branch 'main' into fix/ota-profile-race 2026-07-31 14:14:23 +08:00
Kiss Lorand
54dc5a2f1d Hungarian localization overhaul (#15024) 2026-07-30 15:08:04 -03:00
Kris Austin
303be94262 feat(msix): add execution alias and web link associations to the Store package (#14799) 2026-07-30 10:02:44 -03:00
Ian Bassi
58bad17af9 AI translation update (#15018) 2026-07-29 18:09:32 -03:00
Nathan Schulte
ddce030322 add space after "Arranging" message (#15017) 2026-07-29 17:37:23 -03:00
Ian Bassi
2d4b431d5f Improve dimmed layers (#15001)
Co-authored-by: yw4z <yw4z@outlook.com>
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-07-29 16:27:15 -03:00
Ian Chua
27c468754c feat(plugin): expose orca.host.app_language() for plugin localization (#14997)
# Description

Python plugins currently have no way to localize their own dialogs to
match the app: the UI language is stored in `OrcaSlicer.conf`, which the
plugin audit hook deny-lists by design (the file sits next to cloud
secrets), and the host API exposes no app info. As a result, localized
plugins have to guess the language from the OS locale, which does not
always match the slicer UI (and the embedded interpreter often gets no
`LANG` at all in GUI sessions).

This PR adds a minimal read-only accessor:

```python
orca.host.app_language()   # -> "en_US", "ru_RU", ...
```

It returns `GUI_App::current_language_code_safe()` — only the language
code string, nothing else from the config, so the audit-hook security
model is untouched.

No breaking changes; one file, +10 lines.

# Screenshots/Recordings/Graphs

(screenshots of the test dialog will be attached below)

## Tests

- Built on macOS (arm64, Ninja) with this change — compiles clean.
- Ran a minimal script-capability plugin calling
`orca.host.app_language()` on a system with Russian UI: the dialog shows
`'ru_RU'`.
- Guard before GUI init follows the same exception pattern as the
neighbouring `plater()`/`preset_bundle()` accessors.
<img width="903" height="826" alt="Снимок экрана 2026-07-28 в 23 10 48"
src="https://github.com/user-attachments/assets/27088265-b55d-4958-8602-7c3ab4993003"
/>
<img width="437" height="276" alt="Снимок экрана 2026-07-28 в 23 10 56"
src="https://github.com/user-attachments/assets/9845b401-dc8e-4353-aa24-5ace678270d6"
/>
2026-07-30 01:19:09 +08:00
SoftFever
3ab9cf53d0 code cleanup 2026-07-30 00:55:09 +08:00
Bartok
88632710d5 docs: add Microsoft Store install path for Windows (#15009)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Signed-off-by: Bartok <danielrpike9@gmail.com>
2026-07-29 10:53:20 -03:00
Nathan Schulte
d434e35488 fix STEP progress message percent format (#14994) 2026-07-29 10:48:01 -03:00
yw4z
7bd53b1283 Fix cannot type values to spin control if first digit of desired value is less then min value (#15002) 2026-07-29 10:19:10 -03:00
SoftFever
252df70ec4 Move the WipeTower2 wall gap to the wipe start row for ram toolchanges 2026-07-29 20:58:21 +08:00
Ian Bassi
6489b4cad3 Fix: Only one wall top surfaces (#14929) 2026-07-29 09:23:35 -03:00
SoftFever
086bbf986b Restart the WipeTower2 wipe at the box boundary after multitool ramming
With the gap wall on a multi-tool printer, quantize the ram band up to its
whole reserved rows (as the BBL tower does for the old-tool purge) and start
CP TOOLCHANGE WIPE at the left-edge boundary on a fresh row below it instead
of continuing from wherever the ram serpentine ended. The entry scrub then
runs at the wall gap on ram toolchanges too, and the wipe box is whole rows,
so it is filled completely like the no-ram case. SEMM and skip-points-off
behavior is unchanged.
2026-07-29 19:59:33 +08:00
SoftFever
67e1ce03a6 Scrub the WipeTower2 toolchange entry with the BBL flat-ironing spiral
The entry scrub now matches the BBL tower's toolchange_wipe_new sequence:
after the ironing drag the retracted nozzle runs a dry expanding-square
spiral centred on the wall-gap entry point before resuming the purge row.
The spiral runs whenever the gap wall is on (disable per filament via
filament_tower_ironing_area = 0); WipeTower2 no longer reads
prime_tower_flat_ironing.
2026-07-29 18:48:10 +08:00
SoftFever
36bd453ac8 Tile WipeTower2 purge rows contiguously across toolchange blocks
Without ramming, each purge block reserved one wipe pitch more than its
rows occupy (ceil+1 rounding plus the ram-geometry start offset), and the
wipe began a full pitch inside the block, leaving a blank band of exactly
two pitches between adjacent blocks. Plan the block as whole wipe rows,
start the first row so the row lattice continues across the block
boundary, and fill the reserved box instead of stopping at the ordered
volume, mirroring how the BBL WipeTower keeps planned depth identical to
printed rows. Ram-printing toolchanges (SEMM with ramming enabled,
multitool ramming) are unchanged.
2026-07-29 16:37:06 +08:00
SoftFever
9292db2f9f Reserve WipeTower2 toolchange depth to match the printed purge
The planner reserved ramming rows gated only on enable_filament_ramming and
sized them with the SEMM 0.25s time step, while toolchange_Unload rams on
(semm && enable_filament_ramming) || filament_multitool_ramming with the
multitool time step. Disabling multitool ramming therefore left ~3 unprinted
rows per toolchange as blank bands in the tower. Without ramming the first
wipe line also needs reserved depth of its own (it no longer rides the last
ramming row), plus the y_step/2 offset the wipe start inherits from the
ramming start position - otherwise the tightened boxes truncate the ordered
purge at the box edge.
2026-07-29 13:23:29 +08:00
Kiss Lorand
29d4513694 Fix overlapping brims (#14991) 2026-07-28 17:46:14 -03:00
Ru
0dc14aa876 feat(plugin): expose orca.host.app_language() for plugin localization
Plugins have no way to localize their own dialogs: the UI language lives in
OrcaSlicer.conf, which the plugin audit hook deny-lists because the file sits
next to cloud secrets. Add a read-only host accessor that returns just the
language code (current_language_code_safe), so plugins can match the app
language without touching the config file.
2026-07-28 23:11:05 +03:00
SoftFever
bef47b2c70 Iron the purge start out through the skip-point gap in WipeTower2
Port the BBL tower's entry line ironing: extrude the first 3 mm of the
purge, retract, drag the nozzle 1.5x back out through the wall gap at
F600, creep back at F240 and unretract, so the toolchange start blob
ends up in the gap instead of on the wall. Fires only when the purge
starts at the left-edge entry heading right (in-place toolchangers);
SEMM ram/cooling wipes start mid-box and the priming line has no wall,
so both keep their previous output.
2026-07-28 21:21:50 +08:00
peachismomo
013a9452af fix: prevent Windows OTA vendor profile update race 2026-07-28 19:48:10 +08:00
Noisyfox
5ede9711f5 Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize (#14948)
* For dialog without explicitly `SetMinSize`, we should use `SetSizerAndFit` instead, otherwise the dialog will not show correctly on GTK3. (OrcaSlicer/OrcaSlicer#14561)
- and if `SetSizer` is called before the full layout has been built, then an extra `SetSizeHints` should be called before layout/fit so the min size can be properly set automatically based on children's min sizes accordingly.

* Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize

Replace SetSizer() with SetSizerAndFit() in 11 dialog constructors that
neither call SetMinSize() nor SetSizeHints(), ensuring proper minimum
size propagation from child widgets on GTK3.

SetSizerAndFit internally calls sizer->SetSizeHints(window), which
sets the window's minimum size based on children — the same fix
applied to ProjectDropDialog in 8a7662083e.

Also drop sizer->Fit(this) calls where present, since they only
resize but don't set the min size hint needed by GTK3.

Co-Authored-By: Claude <noreply@anthropic.com>

* Update code style

* Update TroubleshootDialog.hpp

* Fix unsaved preset dialog layout

* Fix MsgDialog layout

* Fix other 3 instances in MsgDialog.cpp

* Fix a few more instances

* Fix printer option dialog too big on Windows

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-07-28 14:32:55 +08:00
Ian Bassi
6bcb809dd0 Calibrations improvements (#14759) 2026-07-27 20:11:44 -03:00
Ian Bassi
33dfb66aa5 Cyclic ordering improvement (#14784) 2026-07-27 19:58:54 -03:00
Maksym Pyrozhok
ef7bfeda9c Cyclic ordering (#13578)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-27 19:52:29 -03:00
Ian Bassi
47ccca7f72 Full Orca translation via AI (tagged) (#14970)
Co-authored-by: Felix14_v2 <75726196+Felix14-v2@users.noreply.github.com>
Co-authored-by: π² <189209038+pi-squared-studio@users.noreply.github.com>
2026-07-27 19:31:33 -03:00
SoftFever
5b475e5e98 Route the in-place toolchange tower entry through the skip-point gap
On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).
2026-07-28 00:15:59 +08:00
SoftFever
56810c8c7f Port the skip-points gap wall to WipeTower2
prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.
2026-07-27 21:18:53 +08:00
SoftFever
c3c37e474a Print the WipeTower2 shell with a non-support, non-soluble filament
Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.
2026-07-27 12:30:57 +08:00
Kiss Lorand
04e13200aa Support bugfixes (#14678) 2026-07-26 19:16:19 -03:00
SoftFever
1696d5ca39 Use the squared rib tower size in arrange estimates
estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.
2026-07-27 03:08:47 +08:00
SoftFever
466c36eaa3 Complete the rib wipe tower port in WipeTower2
The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.
2026-07-27 03:07:07 +08:00
SoftFever
5792fef805 Merge branch 'main' into feature/update_wipetower 2026-07-27 00:51:29 +08:00
SoftFever
bc016af1c9 Fix post-slice self-invalidation on custom multi-extruder printers 2026-07-27 00:49:08 +08:00
SoftFever
7a378d2fc4 Sync WipeTower from BambuStudio(through ca1881761) 2026-07-27 00:48:44 +08:00
Alexandre Folle de Menezes
ed86efcf56 Improve and complement pt-BR translation (#14963) 2026-07-26 12:59:24 -03:00
Kris Austin
306d4b73ef test: stop littering the working directory with debug files (#14785) 2026-07-26 12:53:56 -03:00
SoftFever
f60e0e776e Add back 02.03.00 plugin to support debugging (#14966)
* Add back 02.03.00 plugin to support debugging
2026-07-26 23:49:53 +08:00
SoftFever
d84a211b91 fix crashes when switching bbl printers 2026-07-26 22:15:53 +08:00
Noisyfox
63044b7661 feat: Add layout debugging/inspecting tool (#14919)
* Add wxInspector dep

* Initial intergration of wxInspector

* docs: add wxInspector plugins design spec

Design spec for two wxInspector plugins (DPIAware + CustomWidgets) that expose
OrcaSlicer's custom control properties in the inspector property grid.

Covers: DPIAware scale-factor properties, Button, CheckBox, TextInput,
SwitchButton, ProgressBar, Label, and LabeledStaticBox.

* docs: add wxInspector plugins implementation plan

6-task plan covering: source changes to existing widget headers,
DPIAwarePlugin, CustomWidgetsPlugin, registration helper,
MainFrame/CMake wiring, and build verification.

* feat: add getters/setters for wxInspector plugin access

Add minimal public accessors to DPIAware (set_scale_factor,
set_prev_scale_factor, set_em_unit, force_rescale), Button
(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked),
TextInput (GetCornerRadius), and LabeledStaticBox
(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale).

* feat: add wxInspector plugin registration helper

Add RegisterOrcaInspectorPlugins() inline function that creates
and registers the DPIAwarePlugin and CustomWidgetsPlugin as
static instances (matching wxInspector's built-in pattern).

* feat: add DPIAware wxInspector plugin

Exposes DPI scaling properties (scale_factor, prev_scale_factor,
em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog
widgets. Uses dynamic_cast for detection and a template helper
to capture the correct static type for lambda accessors.

* feat: add OrcaCustomWidgets wxInspector plugin

Exposes Orca-specific properties on 7 widget types:
- Button: Style, Type, Selected
- CheckBox: Half Checked
- TextInput: Label, Text Value, Corner Radius
- SwitchButton: Value
- ProgressBar: Proportion, Show Number
- Label: Is Hyperlink, Font Point Size
- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale

Each widget type uses dynamic_cast for safe detection.

* feat: wire wxInspector plugins into MainFrame and build

Call RegisterOrcaInspectorPlugins() in MainFrame constructor after
SetupInspectorAccelerator(). Add all 5 plugin source files to
SLIC3R_GUI_SOURCES in CMakeLists.txt.

* fix: move plugin registration to GUI_App::on_init_inner

Register plugins once in app init rather than in MainFrame
constructor, which may be recreated during the application
lifetime.

* fix: include plugin headers in Registration.hpp for complete types

Static locals require complete type. Include DPIAwarePlugin.hpp and
CustomWidgetsPlugin.hpp instead of forward-declaring. Also remove
unused include from MainFrame.cpp (registration moved to GUI_App).

* fix: qualify DPIFrame/DPIDialog with Slic3r::GUI namespace

* Make DPIDialog inspectable. For other dialogs, we will add them if necessary later.

* docs: add spec for moving wxInspectable into DPIAware template

Move wxInspector::wxInspectable base class from DPIDialog and MainFrame
into the common DPIAware<P> template, making all DPIAware widgets
automatically visible in the inspector tree.

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: add implementation plan for moving wxInspectable into DPIAware

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: update spec/plan — move SetupInspectorAccelerator into DPIAware too

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware

DPIAware<P> now inherits wxInspector::wxInspectable and calls
SetupInspectorAccelerator in its constructor, making all DPIAware
widgets automatically appear in the inspector tree with the
Ctrl+Shift+I shortcut. DPIDialog now uses 'using' to inherit the
constructor. Remove redundant wxInspectable inheritance and
SetupInspectorAccelerator calls from DPIDialog and MainFrame.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use LB_HYPERLINK constant instead of magic number 0x0020

Co-Authored-By: Claude <noreply@anthropic.com>

* Clean up

* Fix Linux build

* Don't build wxInspector sample

* Use shallow clone

* Try fix flatpak build

* Attempt to fix build again

* Fix build failure caused by 436c16135e

* wxWidgets build only download required submodules

* This should fix build on Windows on ARM

* Enable PIC

* Disable layout inspector by default for public release

* Use wxInspector 1.0.0 release

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-26 20:25:25 +08:00
Kris Austin
11fdb472d6 fix: hide the extruder switch on single-variant printers (#14930) 2026-07-25 18:46:55 -03:00
Kiss Lorand
68ce4da19f Fix overhang fan speed bugs (#14788) 2026-07-25 18:38:21 -03:00
Kiss Lorand
fc5aec425e Fix stale plate membership during plate grid rearrangement (#14850) 2026-07-25 18:08:12 -03:00
SoftFever
a62fb17e03 Remove cloud deletion of owned plugins from the plugin dialog (#14946)
Owned ("Mine") cloud plugins now offer the same local-only Delete as local
plugins: it removes the installed package and leaves the plugin in the cloud,
still reinstallable. Deleting a plugin from the cloud belongs on the plugin hub
and is no longer reachable from OrcaSlicer, so the whole cloud-delete chain is
removed down to the REST binding.

The deleted row is restored locally instead of via a blocking cloud refetch, so
it survives being offline, and it comes back without the deleted package's error
state.
2026-07-25 22:31:40 +08:00
SoftFever
9a72dda79a Fix plugin configuration not taking effect, and improve the plugin config UI (#14944)
# Description

Changing a slicing plugin's configuration had no effect on the sliced
result until you forced a re-slice some other way; it now applies
immediately. Print, printer and filament presets also keep their plugin
configuration separately, so configuring a plugin on one no longer wipes
out what you set on another.

A plugin's custom configuration page gets the same round of improvements
in both the Plugins dialog and the per-preset dialog: it follows the
app's light/dark theme, keeps its state while you edit instead of
resetting under the cursor, and can tell whether it is being edited
globally or for a preset, so "Restore defaults" can be labeled for what
it will actually do. The two bundled examples show this off — Twistify
now ships a custom configuration UI, and Inspector is themed, groups

# Screenshots/Recordings/Graphs



https://github.com/user-attachments/assets/02ca062a-5143-49a3-abe0-a2a040b3a928



## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-25 20:00:56 +08:00
Ian Bassi
d6cb667b89 Localization refactor pt 2 (#14907) 2026-07-24 09:41:24 -03:00
Ian Chua
43725128d3 chore: make console logging optional (#14932)
# Description

Adding optional flag for enabling console logging introduced in #14439.
Console logging not working with LLDB-DAP on VSCode as per #14897
Code changes won't fix this because it is a LLDB-DAP issue documented in
#14909

Use flag `-DUSE_SLIC3R_CONSOLE_LOG=ON` or `-DUSE_SLIC3R_CONSOLE_LOG=OFF`
to enable/disable it.
Alternatively in you VSCode's settings.json, 
```
"cmake.configureSettings": {
   "USE_SLIC3R_CONSOLE_LOG": "OFF"
}
```

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-24 14:23:21 +08:00
SoftFever
51612b504a Silence CMake configure warnings (#14925)
# Description

Remove DEPENDS/empty-COMMAND args that are invalid in the
add_custom_command(TARGET) form (CMP0175), fix the FindDraco.cmake case
mismatch, and opt Boost lookup into upstream BoostConfig via CMP0167 for
the OpenVDB module and the CGAL find.
# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-24 14:13:35 +08:00
Ian Chua
1ce21c98ac chore: make console logging optional 2026-07-24 12:22:57 +08:00
SoftFever
5b32213d44 Silence CMake configure warnings
Remove DEPENDS/empty-COMMAND args that are invalid in the
add_custom_command(TARGET) form (CMP0175), fix the FindDraco.cmake case
mismatch, and opt Boost lookup into upstream BoostConfig via CMP0167 for
the OpenVDB module and the CGAL find.
2026-07-24 00:56:56 +08:00
Felix14_v2
5c4e7c8d0b More updates for Russian localization (#14876)
Co-authored-by: π² <189209038+pi-squared-studio@users.noreply.github.com>
2026-07-23 13:48:58 -03:00
SoftFever
43f38c0eb6 Attempt to fix crash on switching printer (#14870)
# Description

attempt to fix #14851

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 23:24:29 +08:00
Kris Austin
137e8e5e92 docs(tests): add a layered guide to the test tree (#14628) 2026-07-23 09:19:37 -03:00
Ian Chua
f1f65342f0 fix: reserve plugin enable state during rescans (#14860)
# Description

Plugin discovery now distinguishes an unavailable or invalid
.install_state.json from a valid sidecar. During transient filesystem
replacement, the existing enabled state is preserved instead of being
reset, preventing plugins from unexpectedly losing their auto-load
behavior during rescans.

Thanks @WeLizard for pointing this out.

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 12:24:36 +08:00
Ian Chua
738af9f4eb Merge branch 'main' into fix/plugin-install-state 2026-07-23 12:24:19 +08:00
Ian Chua
bbe39629ed fix: plugin bugs (#14855)
# Description

Fixes some bugs reported by users and the community

# Fix 1: Latest Version
If the user updates the plugin version on OrcaCloud without a changelog,
the latest version shown on OrcaSlicer won't be accurate.
## Issue
OrcaSlicer uses the changelog returned by OrcaCloud backend as the
source of truth, even though we query a latest version from the backend
as well. The changelog only consists of version entries that have
changelogs so if the latest one, e.g. 1.2.0 has no changelog, but 1.1.0
has, the latest version will be interpreted as 1.1.0.
## Fix
Don't use the changelog for version tracking, just use it for the
plugins dialog changelog tab.

# Fix 2: Host theme script / window.orca bridge leaks into cross-origin
child iframes
If a plugin's embedded app renders its own `<iframe>` (its real
catalog/dashboard UI, or a third-party auth/payment widget), that child
document also gets, uninvited: the host theme `<style>` block fighting
whatever CSS the child page already defines.
## Issue
OrcaSlicer injects host themed scripts and window.orca bridge into every
page, potentially breaking any cross origin child frames or `<iframes>`
in general.
## Fix
Only inject top level frames by checking `if (window.top !==
window.self) return;`.

# Fix 3: Transferring Slicing Pipeline Plugin config when switching
printers (#14832)
Select an entry in the Process tab's "Slicing Pipeline Plugin" picker,
switch the active printer preset, then click **Transfer** in the
"modified settings" dialog — OrcaSlicer crashes immediately and
repeatably. Changing other settings (e.g. a single scalar option) does
not reproduce it; only this picker does.
## Issue
`slicing_pipeline_plugin` is a vector option (`coStrings`) with an empty
default. `deep_diff` diffs vector options per-index, so selecting a
plugin produced a `"slicing_pipeline_plugin#0"` dirty key instead of a
plain one. Unlike genuine per-extruder options, this key wasn't caught
by the printer-switch filter that discards stale per-extruder changes,
so it got cached and replayed through `ConfigBase::apply_only`'s
`'#'`-indexed branch, which calls `ConfigOptionVector::set_at()` on the
freshly-reloaded (and still empty) destination vector. `set_at()`'s only
empty-vector guard is an `assert()`, which is compiled out of Release
builds, so it dereferences `values.front()` on an empty vector —
undefined behavior, matching the reported ACCESS_VIOLATION.
## Fix
Treat `slicing_pipeline_plugin` as a single atomic value in `deep_diff`
(`Preset.cpp`), same as `printable_area`/`thumbnails`/etc.,
since it isn't actually per-extruder data. It's now diffed and replayed
as a whole option (`ConfigOptionVector::set()`, a plain vector
assignment) instead of the index-based `set_at()` path — removing the
crash unconditionally, regardless of whether the two printers share the
same extruder configuration.

# Fix 4: Stale .whl cache
After a .whl was loaded once, if at runtime, the .whl is replaced with a
new one, the plugin system will use the stale .whl cache.
## Fix
Added an option in the context menu to Reload or Delete Cache and Reload
for locally installed plugins. The assumption here is that users
shouldn't be modify cloud plugins, and if they want to develop on a
subscribed cloud plugin, they should create a local copy of it.

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 12:24:06 +08:00
Alexandre Folle de Menezes
607648c61f Improve and complement ptBR translation (#14847) 2026-07-22 20:53:44 -03:00
Ian Bassi
a7c0ab52f0 Treat nozzle diameter mismatch as a warning (#14890) 2026-07-22 19:09:57 -03:00
Kris Austin
f23ad64303 fix: infill rotation template crashes and misbehaves on raft prints (#14894) 2026-07-22 17:51:55 -03:00
Kris Austin
b8cfa32a41 fix: multi-material slicing crash with line width 0 (#14455) 2026-07-22 16:49:37 -03:00
Kiss Lorand
5edd4963eb Fix acceleration/jerk state after custom G-code (#14613) 2026-07-22 16:30:43 -03:00
Kris Austin
857adad293 fix: startup crash on macOS when a printer has a print host set (#14747) 2026-07-22 15:14:49 -03:00
Ian Bassi
a3ef75586e Update home readme (#14905) 2026-07-22 14:42:32 -03:00
Bartok
3400a49696 docs(readme): remove empty unclosed h3 before community heading (#14834) 2026-07-22 14:32:50 -03:00
Bartok
e08af1294d docs(readme): document macOS Homebrew cask install (#14899)
Signed-off-by: Bartok9 <danielrpike9@gmail.com>
2026-07-22 14:24:23 -03:00
Nathan
b118c03830 locales: update Traditional Chinese translation terminology (#14889) 2026-07-22 14:16:45 -03:00
Neko.vecter
0ff966c9b7 i18n: v2.4.2-zh-cn (#14896) 2026-07-22 14:12:17 -03:00
packerlschupfer
705c82688e PartPlate::check_outside: guard m_plater null deref (fix CLI rotate SIGSEGV) (#14415) 2026-07-22 09:50:32 -03:00
Wegerich
c31a55c6e0 Remove numeric concatenation when offering shared profiles (#14875)
Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-07-22 09:48:28 -03:00
Ian Chua
bc6ae243ca Merge branch 'main' into fix/plugin-bugs 2026-07-22 13:14:03 +08:00
SoftFever
7db0a59cc1 fix: H2C carousel - port BBS NozzleStatusRecorder for per-slot purge tracking (#14800)
## Problem

On H2C (carousel) printers, the wipe tower purge volume calculation in
`_make_wipe_tower()` tracks filament state **per-extruder** (2 slots).
Since H2C has up to 7 carousel nozzle slots on a single extruder, all
filaments sharing that extruder are collapsed into one tracking slot.
This causes:

Test:

[5cubes.3mf.zip](https://github.com/user-attachments/files/30092551/5cubes.3mf.zip)

- **Massive redundant AMS flushing** every filament change on the
carousel triggers a full purge against the "previous" filament, even
when the target nozzle slot already has the correct filament loaded
- **60.9g total weight** instead of ~17g (**3.5× material waste**)
- **3h09m print time** instead of ~1h57m (**60% longer**)

## Root Cause

The code uses `nozzle_cur_filament_ids[extruder_id]` (a 2-element array)
to track which filament was last used for each extruder. BambuStudio
uses `NozzleStatusRecorder`, which tracks per `group_id` (physical
carousel slot 0..6).

## Changes

| File | Change |
|---|---|
| `Print.cpp` | Replace `nozzle_cur_filament_ids` with
`NozzleStatusRecorder`. Use `get_nozzle_for_filament()` to resolve the
physical carousel slot per layer. Select `filament_prime_volume_nc` for
nozzle changes, `filament_prime_volume` for filament changes. |
| `PrintConfig.hpp` | Add `ConfigOptionFloats filament_prime_volume`
(per-filament EC prime volume, missing from upstream but present in BBS
and H2C profiles) |
| `PrintConfig.cpp` | Register `filament_prime_volume` with default
45mm³ (matching BBS) |
| `Preset.cpp` | Add `filament_prime_volume` to preset keys |

Also includes `tests/compare_analyzer/` - two standalone Python tools
for G-code slice comparison and temperature timeline analysis (stdlib
only, no dependencies).

## Test Results (5-color H2C Hybrid print, same 3mf project)

| Metric | Upstream (broken) | **Fixed** | BBS (reference) |
|---|---|---|---|
| **Total weight** | 60.90g | **16.20g**  | 17.47g |
| **Print time** | 3h09m | **1h57m**  | 1h51m |
| **Filament changes** | 105 | 105 | 140 |
| **Tool changes** | 35 | 35 | 35 |
| **Critical discrepancies vs BBS** | ⚠️ YES |  None | — |


## Analysis Tools (`tests/compare_analyzer/`)

Two standalone Python tools (stdlib only, no dependencies) for deep
G-code comparison:

- **`compare_slices.py`** - comprehensive .3mf slice comparison:
filament usage, nozzle mapping, tool change sequences, prime tower
analysis, temperature timeline, retract parameters, and automatic
critical discrepancy detection (weight/time anomalies)
- **`show_temp_plot.py`** - interactive HTML temperature timeline
plotter for visualising heater profiles during multi-nozzle prints
(supports single-file and side-by-side comparison)

Usage:
```bash
python3 tests/compare_analyzer/compare_slices.py file1.3mf file2.3mf --labels "Upstream" "Fixed"
python3 tests/compare_analyzer/show_temp_plot.py file1.3mf file2.3mf
```


## Screenshots

### OrcaSlicer Upstream (unfixed) - 60.90g, 3h09m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 22 58"
src="https://github.com/user-attachments/assets/3efb2bff-ff1e-43db-9669-feafa5921b51"
/>


### OrcaSlicer Fixed - 16.20g, 1h57m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 23 08"
src="https://github.com/user-attachments/assets/c1d36dc5-9b01-4691-80ef-7364540e1f4e"
/>

### BambuStudio Reference - 17.47g, 1h51m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 24 52"
src="https://github.com/user-attachments/assets/5c0b11e2-64f4-40e9-8c7c-3f42519786c3"
/>

### Temperature Timeline: Upstream vs Fixed
<img width="1511" height="829" alt="Screenshot 2026-07-16 at 15 23 35"
src="https://github.com/user-attachments/assets/926c2cb5-dfd4-4ce0-bb40-82e6165eb134"
/>


### Temperature Timeline: Fixed vs BBS

<img width="1512" height="825" alt="Screenshot 2026-07-16 at 15 23 51"
src="https://github.com/user-attachments/assets/85c72dda-e779-4aa6-8118-fb17e5f8482d"
/>


## Compatibility

Safe for non-carousel printers: when each extruder has a single nozzle,
`group_id == extruder_id`, so `NozzleStatusRecorder` behaves identically
to the original per-extruder tracking. The `filament_prime_volume`
default (45mm³) matches the existing global `prime_volume` default.

## Reference
BambuStudio `Print.cpp` `_make_wipe_tower()` L3341-3392 -
`NozzleStatusRecorder` pattern.
2026-07-22 12:59:17 +08:00
Tobias Gloth
b292a06b3f std::set_union expects its inputs to be sorted. (#13928) 2026-07-21 16:14:14 -03:00
Kris Austin
cc1fe80073 fix: out-of-memory on Windows crashes with no dialog and a misleading stack (#14807) 2026-07-21 15:43:55 -03:00
Kris Austin
b86501cd3c fix: guard Bambu Lab dev-mode dialog against repeat spam (#14774) 2026-07-21 15:40:40 -03:00
packerlschupfer
ae0193899d calib: default-init Calib_Params scalar fields (#14413) 2026-07-21 15:28:36 -03:00
Kris Austin
5d9b641093 fix: guard filament_printable read against a short per-filament array (#14695) 2026-07-21 15:24:15 -03:00
Kiss Lorand
8390d55127 Fix crash when opening Object Settings with mixed nullable values (#14864) 2026-07-21 15:17:22 -03:00
Ian Bassi
23f8ad88d1 Troubleshoot Center Wiki redirection (#14887) 2026-07-21 15:16:07 -03:00
Rodrigo Faselli
21f830bf24 Ensure spiral lift inside print area (avoid collision) (#13634)
* Ensure spiral lift positive quadrant

* check for printable area

* clamp area

* simpler version

* Adjust printable area bounds with safety safety margin

Added safety margin to printable area bounds calculations.

* increase safety margin

* Refactor safety margin calculations in GCodeWriter

* New Logic

Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com>

---------

Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com>
2026-07-21 14:23:41 -03:00
Kris Austin
dfb93e0332 fix: PA pattern calibration over-retracts with absolute E distances (#14473) 2026-07-21 12:42:09 -03:00
SoftFever
a5be8075e9 Don't auto-connect LAN printers on Device tab entry (#14851)
Adopt BambuStudio's load_last_machine (cloud machines only, remembered
machine preferred) and port record_user_last_machine /
get_user_last_machine. Orca's version auto-connected an arbitrary LAN
printer, starting an unrequested connection that the user's first
printer switch tore down mid-flight, which the 02.08 plugin's connect
worker does not survive. This also caused the 100% crash-on-relaunch
loop: the crashed-on printer was auto-connected at startup, so
re-selecting it always hit the same-machine disconnect+reconnect path.
2026-07-21 13:33:48 +08:00
Ian Chua
b121051220 Merge branch 'main' into fix/plugin-bugs 2026-07-21 12:56:52 +08:00
Ian Chua
4c2d9b4b18 Merge branch 'main' into fix/plugin-install-state 2026-07-21 12:55:30 +08:00
SoftFever
8d528b2a8f Attempt to fix crash when switching printers in the Device tab (#14851) 2026-07-21 03:49:03 +08:00
Ian Chua
28feb8480e fix: reserve plugin enable state during rescans 2026-07-20 18:40:31 +08:00
SoftFever
203ae1588e Merge branch 'main' into feature/h2c_support_clean_fixes 2026-07-20 17:36:16 +08:00
SoftFever
9b5eb76478 Misc updates and clean up
- WipeTower: use filament_ramming_volumetric_speed(_nc) for ramming, falling back to
  max_vol_speed only when nil; gate precool temps on enable_pre_heating
- ToolOrderUtils: disable the inter-layer forecast in the per-nozzle base reorder so
  H2D/H2C ordering is unchanged
- PrintConfig: stop stripping filament_prime_volume in handle_legacy; document that
  prime_volume drives the Type2 wipe tower and filament_prime_volume the Type1 one
- GCodeProcessor: exclude post-print end-gcode M400 dwells from the M73 estimate and
  drop the dead air-filtration state
- Trim verbose BambuStudio source-location comments across the port
2026-07-20 17:34:13 +08:00
Ian Chua
242e100feb fix: stale .whl cache when .whl changes, added option to delete cache and reload 2026-07-20 14:47:26 +08:00
Ian Chua
2a46bd1384 fix: crash transferring Slicing Pipeline Plugin changes across a printer switch 2026-07-20 13:58:57 +08:00
Ian Chua
ddbbc7f7f2 fix: only the top level frame should have the orca syles and bridges injected 2026-07-20 12:48:17 +08:00
Ian Chua
3a48086f5d fix: plugin descriptor should not reply on changelog for latest version 2026-07-20 12:47:18 +08:00
SoftFever
65ab9b61a8 Merge branch 'main' into feature/h2c_support_clean_fixes 2026-07-20 00:02:43 +08:00
denis svinarchuk
d6b9f0e5d4 fix(H2C): port BBS is_extruder_stat_synced() for sync dialog
Replace hand-rolled nozzle type comparison + Hybrid hack with
BBS-style NozzleGroupInfo comparison in check_ams_status_impl.

Previous approach: direct nozzle_volume_type == printer_flow_type
with a Hybrid tolerance lambda. This either suppressed the dialog
entirely (Hybrid always matched) or showed it on every Preview switch.

New approach (matching BBS):
- Build preset NozzleGroupInfo from extruder_nozzle_stats config
- For Hybrid presets: expand into per-type counts (Std#N, HF#M)
- nozzle_count==0 (never synced): dialog appears for first sync
- nozzle_count>0 (after sync): compare with printer GetNozzleGroups()
- Counts match → dialog suppressed on Prepare↔Preview switches

Safe for all multi-extruder printers (H2C, H2D): non-Hybrid presets
use single NozzleGroupInfo per extruder. Single-extruder printers
exit at is_multi_extruders() guard before reaching this code.

Reference to BBS: BambuStudio/src/slic3r/GUI/Plater.cpp
is_extruder_stat_synced() line 16642
2026-07-17 11:00:16 +01:00
denis svinarchuk
d6f57b3066 perf: enable inter-layer forecast in single-nozzle flush ordering
Enable use_forcast in reorder_filaments_for_minimum_flush_volume_base
to match the multi-extruder path behavior (line 1227).

The forecast solver (solve_extruder_order_with_forcast) considers the
next layer's filament set when choosing ordering for the current layer,
minimizing inter-layer transition flush cost.

Previously disabled (hardcoded false) in the single-nozzle/base path,
causing suboptimal inter-layer transitions. The multi-extruder path
already had this enabled.

Measured on 5cubes (5 filaments, 35 layers, H2C):
- Print time: -12 min (-10%)
- Waste filament: -5g (-28%)
- WT extrusion: -44%

Limited to ≤5 filaments per nozzle per layer (O(N!×M!) complexity).
2026-07-17 02:08:53 +01:00
denis svinarchuk
ba21ed0a12 show_temp_plot: add precool zone visualization
- Add precool (cooldown) zone rendering on departing nozzle panels
- Lime green color for precool, crimson for preheat
- Unified badge-style tooltips (Pheat/Pcool/TC/Wipe)
- Filter invalid precool events (S0, <2s duration)
2026-07-16 22:43:03 +01:00
denis svinarchuk
11f900aa2e WipeTower: add per_cooling_max_speed clamping for H2C extruder changes
Clamp ramming speed during extruder changes so the departing nozzle
has enough time to reach precool_target_temp before carousel rotation.
Only applies to extruder changes (not carousel nozzle changes).

Reference to BBS: BambuStudio/src/libslic3r/GCode/WipeTower.cpp
ramming() L3449-3462
2026-07-16 22:40:27 +01:00
denis svinarchuk
da2ea10fe4 show_temp_plot: scale physical timeline by M73 trapezoid estimate
Physical dist/speed ignores acceleration/deceleration, giving
underestimated total time (29 min vs real 48 min). M73 from the
trapezoid planner accounts for accel/decel and is closer to reality.

Now we keep physical DISTRIBUTION (proportions per-filament) but
scale the X-axis so total time matches M73 trapezoid estimate.
2026-07-16 21:44:50 +01:00
denis svinarchuk
9f394df985 fix(show_temp_plot): use physical G1 motion time for timeline instead of M73
Replace M73-based timeline interpolation with physical time calculation
from G1 feedrates and M400 delays. M73 has 1-minute resolution and
non-uniform granularity which distorts the time axis (e.g. P83→P100
jump makes last filament appear much longer than it actually is).

Physical timeline computes cumulative time per gcode line from actual
move distances and feedrates, giving accurate filament duration on plot.
Falls back to M73 interpolation when raw gcode lines are not available.
2026-07-16 21:06:05 +01:00
denis svinarchuk
403292cb63 fix(GCodeProcessor): exclude end gcode M400 delays from M73 time estimation
End gcode contains firmware-conditional M400 waits for air purification,
timelapse capture, and sound notification that are post-print operations.
These were incorrectly included in M73 total time, inflating the estimate.

The fix detects MACHINE_END_GCODE_START tag during the streaming parse
(process_tags) and sets m_skip_end_gcode_delays=true. process_M400 then
skips timed delays (S/P params) in the end gcode scope.

BBS achieves the same effect by dropping leftover in calculate_time
(is_final=true). We skip at the source instead, which is more surgical
and leaves calculate_time behavior unchanged for all printers.

Affects all BBL printers with MACHINE_END_GCODE_START tag.
Non-BBL printers are unaffected (no tag = no skip).
2026-07-16 20:49:36 +01:00
denis svinarchuk
fc705612a4 show_temp_plot: fix M620 timeline weight for accurate toolchange duration
The M620→M621 firmware toolchange block weight (500x) was only applied
to sparse track sample lines. Hundreds of G1 moves between samples
inside the M620 block got weight=1, causing firmware toolchange to
appear compressed on the timeline plot.

Build continuous M620→M621 line ranges from track samples and apply
weight=500 to ALL lines within those ranges. This makes toolchange
and wipe tower zones proportionally accurate on the timeline.
2026-07-16 18:59:34 +01:00
denis svinarchuk
b0dddb4648 wipe_tower: carousel barrier parity with BBS (precool, ramming speed, reverse travel)
Match BBS ramming() L3421-3535 and toolchange_wipe_new L4039-4044:

nozzle_change_new (carousel = !extruder_change):
  - M632 barrier filled: M400 + M104 precool + M106 fan (BBS L3425-3428)
  - Carousel-specific ramming speed via max_e_ramming_speed.second (BBS L3435)
  - Reverse travel after ramming via ramming_travel_time (BBS L3499-3526)
  - Heater index via m_physical_extruder_map (matches BBS get_extruder_id)

toolchange_wipe_new (add_M104_by_requirement):
  - format_line_M104: M400+M104 instead of M632+M104+M633 (BBS L1328-1341)
  - No carousel suppression: fires for both extruder and carousel changes (BBS L4040-4044)
  - m_is_multiple_nozzle gate retained (Orca calls this for all printers; BBS has it H2C-only)

FilamentParameters (WipeTower.hpp):
  - Added: max_e_ramming_speed, ramming_travel_time, precool_target_temp, precool_t
  - Added: is_need_reverse_travel() method

Init (WipeTower.cpp):
  - Carousel params from config: ramming speed, precool temp/timing, travel time
  - Matches BBS init L1878-1932

Non-H2C safety: all carousel paths gated by m_is_multiple_nozzle (false for non-H2C).
Ramming speed fallback chain ensures identical values for non-H2C printers.
2026-07-16 18:36:39 +01:00
denis svinarchuk
b90ac13d86 fix(H2C): carousel nozzle change zone — ramming depth, M632 barriers, preheat suppression
- Add nozzle_change_depth override for carousel rotations (same extruder,
  different nozzle) in plan_toolchange() and plan_tower_new() — previously
  depth was 0 because condition checked m_filament_map (extruder-level)
  instead of m_filament_nozzle_map (nozzle-level)
- Emit M632/M633 carousel barriers inside NOZZLE_CHANGE_START/END markers
  in nozzle_change_new(), matching BBS ramming() L3421-3429 pattern
- Add is_extruder_change flag to NozzleChangeResult (matches BBS field)
  to propagate carousel/extruder-change distinction downstream
- Suppress duplicate M632 in add_M104_by_requirement for carousel —
  M632 already emitted inside nozzle_change_new(); extruder change
  preheat M104 preserved (BBS has no add_M104_by_requirement at all)

Reference to BBS: BambuStudio/src/libslic3r/GCode/WipeTower.cpp
  ramming() extruder_change flag, is_need_ramming(), plan_toolchange()
2026-07-16 18:00:22 +01:00
denis svinarchuk
8dd3f784be chore: remove accidentally staged embedded test repo 2026-07-16 15:49:48 +01:00
denis svinarchuk
4f62778b1b chore: remove screenshots from repo (use PR attachments instead) 2026-07-16 15:48:23 +01:00
denis svinarchuk
18f62e634e docs: add H2C purge regression screenshots for PR
Screenshots comparing upstream (60.9g), fixed (16.2g), and BBS reference (17.5g)
slicing results, plus temperature timeline plots.
2026-07-16 15:26:43 +01:00
denis svinarchuk
6a584c7c79 test: add compare_analyzer tools for G-code slice comparison
Add two standalone Python tools for deep comparison and analysis of .3mf
slicing project files:

- compare_slices.py: comprehensive slice comparison with filament usage,
  nozzle mapping, tool change sequences, prime tower analysis, temperature
  timeline, and automatic critical discrepancy detection
- show_temp_plot.py: interactive HTML temperature timeline plotter for
  visualizing heater profiles during multi-nozzle prints

Both tools use only Python stdlib (no external dependencies).
Primary use case: regression testing H2C carousel purge volumes and
BBS compatibility verification.
2026-07-16 15:22:26 +01:00
denis svinarchuk
407c78fb30 fix: port BBS NozzleStatusRecorder pattern for per-carousel-slot purge tracking
The upstream _make_wipe_tower() tracked purge volumes per-extruder (2 slots),
which collapsed all H2C carousel filaments into one slot and caused massive
redundant AMS flushing (~40g instead of ~0.4g).

Changes:
- Print.cpp: Replace per-extruder nozzle_cur_filament_ids with BBS
  NozzleStatusRecorder that tracks per group_id (carousel slot 0..6).
  Use get_nozzle_for_filament() to resolve physical slot per layer.
  Select filament_prime_volume_nc for nozzle changes, filament_prime_volume
  for filament changes (BBS pattern).
- PrintConfig.hpp/cpp: Register filament_prime_volume (per-filament EC prime
  volume, default 45mm³) matching BBS PrintConfig.
- Preset.cpp: Add filament_prime_volume to preset keys list.

Safe for non-carousel printers: group_id == extruder_id when each extruder
has one nozzle, so NozzleStatusRecorder behaves identically to the original
per-extruder tracking.

Reference to BBS: BambuStudio/src/libslic3r/Print.cpp _make_wipe_tower() L3341-3392
2026-07-16 14:54:46 +01:00
325 changed files with 67517 additions and 49941 deletions

View File

@@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.12'

View File

@@ -17,7 +17,7 @@ cmake --build . --config %build_type% --target ALL_BUILD -- -m
## Testing
Catch2 framework. Tests in `tests/` directory.
Catch2 framework. Tests in `tests/`; see [tests/AGENTS.md](tests/AGENTS.md) for where a new test belongs and the conventions to follow.
```bash
cd build && ctest --output-on-failure # all tests
@@ -30,6 +30,7 @@ ctest --test-dir ./tests/fff_print
- C++17, selective C++20. PascalCase classes, snake_case functions/variables
- `#pragma once` for headers. Smart pointers and RAII preferred
- Parallelization via TBB — be mindful of shared state
- Always use `SetSizerAndFit(sizer)` instead of `SetSizer(sizer)` on top level window. Unless `SetSizer` must be called before the full layout is built, call `sizer->SetSizeHints(window)` afterwards in this case.
## Key Entry Points
@@ -59,7 +60,31 @@ ctest --test-dir ./tests/fff_print
## Localization & translations
- Translation catalogs live in `localization/i18n/<lang>/OrcaSlicer_<lang>.po`.
- When creating or reviewing translations, use the [Localization glossary](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/guides/localization_glossary.md) as the source of truth for recurring terms, so the same English term is always rendered the same way within a language and terms that must stay in English (brand/product names, acronyms, file formats, G-code, macros/variables) are not translated.
- If a term's established translation changes, update both the affected `.po` files and the glossary so they stay in sync.
- Only edit `msgstr` (never `msgid`); keep placeholders (`%s`, `%1%`, `\n`), context (`msgctxt`), and file encoding/line endings intact.
Catalogs live in `localization/i18n/<lang>/OrcaSlicer_<lang>.po`; the template is `OrcaSlicer.pot`.
See the [Localization guide](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/guides/localization_guide.md) for the human-facing version of these principles.
### Terminology
- Use the [Localization glossary](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/guides/localization_glossary.md) as the source of truth for recurring terms, so the same English term is always rendered the same way within a language, and terms that must stay in English (brand/product names, acronyms, materials, file formats, G-code tokens, macros/variables/identifiers) are not translated.
- If a term's established translation changes, update both the affected `.po` files and the glossary (`localization_glossary.tsv`, then regenerate) so they stay in sync.
- Translate the *meaning*, not the words. Check what the string actually controls before translating it — English reuses one word for different things. `Flow ratio` (multiplier), `Flow Rate` (throughput) and `Flow Dynamics` (pressure compensation) are three different terms; `extruder` may mean the toolhead, the feeder motor, or the nozzle depending on the string.
- Reuse one template per recurring message shape (`Failed to connect to …`, `Are you sure you want to …?`), even where the English wording varies.
### Editing rules
- Only edit `msgstr`**never** change `msgid`, and never "fix" wrong English in the translation alone. Report the source string instead.
- Preserve exactly: placeholders (`%s`, `%d`, `%1%`, `%zu`, `%%`), every `\n` (count *and* position, including leading/trailing), leading/trailing spaces, HTML tags, `℃`, and the file's encoding and line endings.
- **Never reorder positional arguments** in a `c-format` string. If the msgid is `%d` then `%s`, that order must hold — swapping them breaks at runtime.
- `msgctxt` separates homonyms — always read it. `Back`/`Camera View` is the rear view of the 3D navigator, while `Back`/`Navigation` is the go-back button; `Top` exists in the *Alignment*, *Layers* and *Camera View* senses.
- When a string needs disambiguating, add context in the source (`_L_CONTEXT`/`_u8L_CONTEXT`), don't work around it in the translation.
- A literal `%` inside a string xgettext flagged `possible-c-format` will fail `msgfmt`. Fix it with a `// xgettext:no-c-format, no-boost-format` comment above the string in the source — do not mangle the translation or use `%%` in text that is never passed through printf.
- Plural entries: read `nplurals` from the catalog's `Plural-Forms` header (it is **not** always 2 — ja/ko/zh/th/vi use 1, ru/cs/pl/lt use 3, uk uses 4). Each form must be genuinely inflected for its quantity; repeating one sentence across all forms is a bug in Slavic/Baltic languages, though it is correct for Turkish and Hungarian.
- An entry whose `msgstr` equals its `msgid` is untranslated even though it is not empty; a plural entry with any empty form is likewise incomplete.
- Mark machine-produced translations with an `# AI Translated` translator comment. Don't add it to a human translation you didn't actually rewrite.
- Don't reflow or re-wrap unrelated entries — keep the diff limited to the strings you changed.
### Verifying
- `scripts/run_gettext.bat --full` (Windows) regenerates the template, merges every catalog and compiles the `.mo` files. It must exit 0.
- Or check a single catalog with `msgfmt --check-format -o <out>.mo localization/i18n/<lang>/OrcaSlicer_<lang>.po`.
- Fuzzy entries are not shown to users. If you correct one, clear its `fuzzy` flag, otherwise the fix never ships.

View File

@@ -835,7 +835,7 @@ endif ()
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/i18n")
set(BBL_L18N_DIR "${CMAKE_CURRENT_SOURCE_DIR}/localization/i18n")
add_custom_target(gettext_make_pot
COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_CTX:1,2c --keyword=_CTX_utf8:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost
COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_CONTEXT:1,2c --keyword=_u8L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost
-f "${BBL_L18N_DIR}/list.txt"
-o "${BBL_L18N_DIR}/OrcaSlicer.pot"
COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR}
@@ -853,7 +853,6 @@ foreach(po_file ${BBL_L10N_PO_FILES})
add_custom_command(
TARGET gettext_merge_po_with_pot PRE_BUILD
COMMAND msgmerge -N -o ${po_file} ${po_file} "${BBL_L18N_DIR}/OrcaSlicer.pot"
DEPENDS ${po_file}
)
endforeach()
add_custom_target(gettext_po_to_mo
@@ -869,7 +868,6 @@ foreach(po_file ${BBL_L10N_PO_FILES})
TARGET gettext_po_to_mo PRE_BUILD
COMMAND msgfmt ARGS --check-format -o ${mo_file} ${po_file}
#COMMAND msgfmt ARGS --check-compatibility -o ${mo_file} ${po_file}
DEPENDS ${po_file}
)
endforeach()

View File

@@ -10,7 +10,6 @@
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
<h3>
# Official links and community
@@ -90,11 +89,22 @@ Visit our GitHub Releases page for the latest stable version of OrcaSlicer, reco
🌙 **[Download the Latest Nightly Build](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds)**
Explore the latest developments in OrcaSlicer with our nightly builds. Feedback on these versions is highly appreciated.
### Belt Printer Builds
The [nightly release](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds) ships **two parallel builds**: the standard build and a belt-printer build. Both are attached to the same release — tell them apart by the filename suffix:
- **Standard** — no suffix (e.g. `OrcaSlicer_Windows_Installer_x64_nightly.exe`)
- **Belt** — `_belt` suffix (e.g. `OrcaSlicer_Windows_Installer_x64_nightly_belt.exe`)
The `_belt` builds add **experimental support for belt / conveyor (infinite-Z) printers**, where the model is sliced against a tilted belt surface instead of a flat horizontal bed. They include ready-to-use belt printer profiles, the full belt slicing pipeline (mesh rotation and G-code transforms), belt-aware support generation, and a tilted-bed preview.
> ⚠️ Belt printer support is under active development and is **not yet merged into `main`** — it currently ships only in these parallel `_belt` builds, produced from the [`belt-printer`](https://github.com/OrcaSlicer/OrcaSlicer/tree/belt-printer) branch. See tracking PR [#14394](https://github.com/OrcaSlicer/OrcaSlicer/pull/14394) and the original documentation in [#12998](https://github.com/OrcaSlicer/OrcaSlicer/pull/12998).
# How to install
## Windows
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases). Both `x64` and `arm64` installers are published — pick the one matching your CPU.
- *For convenience there is also a portable build available.*
<details>
@@ -109,7 +119,11 @@ Download the **Windows Installer exe** for your preferred version from the [rel
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
</details>
Windows Package Manager
### Microsoft Store
Install from the [Microsoft Store](https://apps.microsoft.com/detail/9mv6gl23xm59) when you prefer a Store-signed package (helps on Windows 11 Smart App Control).
### Windows Package Manager
```shell
winget install --id=SoftFever.OrcaSlicer -e
@@ -117,7 +131,7 @@ winget install --id=SoftFever.OrcaSlicer -e
## Mac
1. Download the DMG for your computer: `arm64` version for Apple Silicon and `x86_64` for Intel CPU.
1. Download the universal DMG, which runs on both Apple Silicon and Intel Macs.
2. Drag OrcaSlicer.app to Application folder.
3. *If you want to run a build from a PR, you also need to follow the instructions below:*
@@ -142,6 +156,14 @@ winget install --id=SoftFever.OrcaSlicer -e
![mac_security_setting](./SoftFever_doc/mac_security_setting.png)
</details>
### Homebrew Cask
```shell
brew install --cask orcaslicer
```
The [Homebrew cask](https://formulae.brew.sh/cask/orcaslicer) installs the official macOS DMG from [GitHub Releases](https://github.com/OrcaSlicer/OrcaSlicer/releases).
## Linux
### Flathub (Recommended)
@@ -190,8 +212,8 @@ resolution: 0.1
# Supports
**OrcaSlicer** is an open-source project and I'm deeply grateful to all my sponsors and backers.
Their generous support enables me to purchase filaments and other essential 3D printing materials for the project.
**OrcaSlicer** is an open-source project, and we're deeply grateful to all our sponsors and backers.
Their generous support helps fund filaments and other essential 3D printing materials for the project.
Thank you! :)
## Sponsors
@@ -215,7 +237,7 @@ Thank you! :)
**Ko-fi supporters** ☕: [Backers list](https://github.com/user-attachments/files/16147016/Supporters_638561417699952499.csv)
## Support me
## Support the project
<a href="https://github.com/sponsors/SoftFever"><img src="https://img.shields.io/badge/GitHub%20Sponsors-30363D?style=flat&logo=GitHub-Sponsors&logoColor=EA4AAA" height="50"></a>
<a href="https://ko-fi.com/G2G5IP3CP"><img src="https://img.shields.io/badge/Support_me_on_Ko--fi-FF5E5B?style=flat&logo=ko-fi&logoColor=white" height="50"></a>

View File

@@ -1,551 +0,0 @@
# Texture Displacement - Technical Notes
Branch: `feature/texture_displacement`. This document is a knowledge dump of the whole feature as
it stands: architecture, file map, algorithms, known bugs found and fixed (with root causes worth
remembering), and what's still deferred. Written so a fresh session (or a fresh pair of eyes) can
pick this up without re-deriving everything from scratch.
## What it does
A paint-style gizmo (`GLGizmoTextureDisplacement`) that lets you:
- Paint one or more "layers" onto a model's surface, each a height-map texture with its own
depth/tiling/rotation/offset/invert/tile-mode/projection-mode/blend-mode.
- Pick a texture from a shipped library (`resources/textures/displacement/`) or import your own
(saved into `<data_dir>/textures/displacement/`, kept separate so app updates can't clobber it).
- Combine overlapping layers with image-editor-style blend modes (Add/Subtract/Multiply/Divide).
- Preview the true displaced result live, before baking (background job, not on the UI thread).
- Optionally preview via a fast GPU bump-map shader instead (no real geometry movement, just
shading) for a lighter-weight alternative.
- Bake into real mesh geometry on demand, restricted to the painted area only.
- Subdivide a low-poly model first so there are enough vertices to show fine detail.
- Unwrap a painted patch with a real CGAL LSCM parameterization and view it in a dedicated,
dockable 2D "UV Editor" pane.
## Architecture
### Data model (per `ModelVolume`)
Each of up to `TEXTURE_DISPLACEMENT_MAX_LAYERS` (8) layers gets its **own independent
`FacetsAnnotation`** paint mask - the exact same `TriangleSelector`/`FacetsAnnotation` machinery
every other paint gizmo (FdmSupports, Seam, MMU, FuzzySkin) already uses, just one full instance
per layer slot instead of one per volume. This is what makes "layered/blended" painting work for
free: the same triangle can be `ENFORCER` in layer 2's mask and layer 5's mask simultaneously, and
at bake/preview time each layer displaces the surface left by the previous one (image-editor-layer
semantics).
### Bake algorithm (`libslic3r/TextureDisplacement.cpp`)
`build_texture_displacement(base_mesh, layers, facets_data, options)` is **accumulate-then-displace,
and topology-preserving**: the returned mesh has exactly the input's vertices and triangles, in the
same order - only the positions of displaced vertices differ.
1. `its_compactify_vertices()` on a copy of the input. In practice a no-op (it only drops
*unreferenced* vertices, and preserves the order and indices of the rest). It is there to
guarantee the index alignment step 3 depends on.
2. Area-weighted vertex normals of the **undisplaced** mesh, computed once. Every layer both
projects and displaces along these, so a vertex covered by several layers moves along one single
well-defined direction. Where the paint does *not* cover every triangle around a vertex, the normal
is recomputed from the painted triangles alone (the union over all layers, so it stays one direction
per vertex). On the rim of a fully painted top face the whole-mesh normal is the 45 degrees bisector
it shares with the side wall, and displacing along that flares the rim outwards instead of raising
it. Interior vertices are unaffected - all their triangles are painted, so the two coincide. Paint
coverage per original triangle comes straight off `TriangleSplittingData::triangles_to_split`.
3. For each layer in slot order: deserialize its stored paint mask into a `TriangleSelector` against
the **base mesh** (never against a previous layer's output), then
`selector.get_facets_strict(ENFORCER)` → the painted patch. Two facts are exploited:
- `get_facets_strict()` returns the mesh's **entire** referenced vertex array regardless of which
state was asked for - only `.indices` is filtered by state. So `get_facets_strict(ENFORCER)`
and `get_facets_strict(NONE)` share identical vertex indexing, which is what lets boundary
detection be a plain index check instead of a position-hash lookup.
- The selector's vertex array *starts with* the mesh's own vertices (extra ones created where a
brush stroke split a triangle are appended after them), and `get_facets_strict()` emits the
referenced ones in order. Combined with step 1, **selector vertex index `i` is our vertex `i`**.
Split vertices live past the end of our array and are simply skipped - they sit on the paint
boundary anyway (splitting only happens at partial coverage), so they would be pinned regardless.
4. A vertex used by at least one **unpainted** triangle is a border vertex. Whether it moves is
`TextureDisplacementOptions::displace_border`, and it **does by default**. The original design
pinned it, justified as stopping the patch tearing away from the surrounding surface - which stopped
being true the moment the bake became topology-preserving. There is no seam to tear: a border vertex
is *one* vertex shared by both regions, and moving it simply tilts the unpainted triangles that use
it. What pinning actually does is clamp the outermost ring of relief to zero, so on a fully painted
face the pattern collapses into a ring of steep ramps right at the edge - the reported "it doesn't
extrude at the border" artifact. Pinning is kept as an option for the case where the relief must not
spill past the paint at all. Either way the border still drives the `edge_smoothing` falloff.
5. Per interior vertex: sample the height texture (`sample_layer_height()`, see Projection methods)
and fold `height * depth_mm * (invert ? -1 : 1)` into that vertex's running total via the layer's
`TextureBlendMode` (see Blend modes). A `visited` set makes each layer fold in exactly **once**
per vertex, no matter how many of the patch's triangles share it - otherwise a Multiply/Subtract
layer would apply two or three times over depending on local triangle fan-out.
6. Finally, move each touched vertex along its (step 2) normal by its accumulated total.
7. Optionally (`TextureDisplacementOptions::smooth_*`) relax the result - see Post-process smoothing.
### Post-process smoothing
`smooth_mesh_vertices(mesh, movable, strength, iterations)` - plain Laplacian relaxation, run after all
layers have been folded in, restricted to the vertices flagged in `movable`. Each pass moves a movable
vertex a `strength` fraction of the way to the average of its one-ring, read from a **snapshot** of the
previous pass so the result does not depend on vertex order (a Gauss-Seidel sweep would smooth several
times as hard at the end of the array as at the start). Neighbours come from a CSR-style adjacency built
once per call.
Its job is to round off the hard steps a bitmap height map leaves behind, which is a different knob from
`TextureDisplacementLayer::smoothing` - that blurs the *height map* before it is ever sampled, this
relaxes the *geometry* afterwards. Topology-preserving, like the bake.
Two ways in, sharing one set of settings on the volume:
- The **"Smooth result"** checkbox + "Smoothing (%)" / "Passes" ride along with Preview and Bake.
`movable` is exactly the set of vertices the displacement moved, so the untouched part of the model
keeps its exact geometry and the ring just outside the displaced set anchors the relaxation (the
relief cannot creep outward).
- **"Smooth baked mesh now"** (`GLGizmoTextureDisplacement::smooth_model()`) applies the same settings to
the volume's *committed* geometry, for relief that is already baked in. `movable` there is the painted
triangles' vertices. Because smoothing never touches the triangle list, this is the one geometry
operation in the gizmo that keeps **every** paint channel verbatim - it saves and restores the eight
texture-displacement masks around `set_mesh()` rather than remapping or dropping them.
**"Ignore outer ring"** (`smooth_skip_border`, **on by default**) drops the patch's own outermost ring of
vertices from `movable`. That ring's neighbours *outside* the paint never move, so relaxing it drags the
rim of the relief back down toward the flat surface and the pattern comes out half-melted exactly where
it meets the edge - crisp everywhere else, which is what makes it look like a bug rather than a setting.
Held out, the border keeps the full depth the texture asked for and only the interior relaxes. Turning it
off softens the outer edge deliberately (a blunter version of the per-layer edge-smoothing falloff).
Note this is the *smoothing* rim, a separate question from whether that rim is displaced at all
(`displace_border`, step 4 above) - the two are independent and both default to "keep the border sharp".
### Blend modes
`TextureBlendMode` {Add, Subtract, Multiply, Divide}, per layer, applied per vertex against the
total accumulated by the layers **below** it (lower slots). The quantity blended is a signed
displacement in **mm**, not a pixel value.
Add/Subtract are self-explanatory. Multiply/Divide are *scaling* operations and so need a unit
convention: they treat the layer's own value as a **factor relative to 1 mm**. That makes `depth_mm`
a gain, and - the property that makes a Multiply layer usable as a mask - a layer with depth 1 mm
sampling a white (1.0) texel multiplies by exactly 1, i.e. leaves the layers below unchanged.
Divide floors its divisor's magnitude at 0.05 - a black texel samples to *exactly* zero, so the
divisor really does hit zero in ordinary use, and an unbounded `1/0` would fling vertices thousands
of mm away and poison the mesh's bounding box (and every plate/print-volume check downstream). The
floor doubles as a cap on how far Divide can amplify the relief beneath it: at most 20×.
The **lowest painted layer ignores its blend mode**: it has nothing beneath it, and Multiply/Divide
against an implicit zero base would annihilate (or blow up) it. Enforced in `build_texture_
displacement()` (the first layer to reach a given vertex always folds in additively) and surfaced in
the UI, which labels that layer "Base layer" instead of offering a control that silently does nothing.
### Projection methods
Four choices per layer (`TextureProjectionMethod`), all funneling through `apply_uv_transform()`
(scale by `1/tiling_scale`, rotate by `rotation_deg`, add `offset`). They are dispatched by
`sample_layer_height()`, which returns a **height**, not a UV - because Triplanar takes three
texture samples per vertex and so has no single UV that represents it.
- **Triplanar** (default) - samples the texture on all three world planes (`(y,z)`, `(x,z)`, `(x,y)`)
and blends the three by the vertex's own normal raised to `TRIPLANAR_BLEND_SHARPNESS` (4).
This is the fix for a real, user-reported bug. The previous version *hard-picked* the single axis
most aligned with the normal, which is discontinuous wherever that dominant axis flips: on a +X
face the planar coordinate is `(y, z)`, on a Y face it is `(x, z)`, so at the shared edge `u`
jumps from `y_edge` to `x_edge`. On a box centred near the origin those two happen to **agree** at
the (+,+) and (,) corners and **differ by the full corner width** at the (+,) and (,+) corners
- which is exactly the "two bad corners, two good ones" symmetry that was observed. A weighted
blend is continuous across the transition by construction, since the weight of the axis being left
behind falls smoothly to zero. (Note this removes the hard *seam*; some cross-fade blurring in the
band right at a 90° edge is inherent to triplanar mapping. A genuinely seam-free wrap around a box
needs a real unwrap - that is what the LSCM mode is for.)
- **Cylindrical** - wraps around an axis through the patch centroid, axis auto-picked as the world
axis *least* aligned with the average normal (perpendicular to the outward radial normal, as a
cylinder's own axis would be). `u = angle * local_radius` (arc length in mm), `v = distance along
axis`. Approximation, not an exact fit for arbitrary geometry.
- **Spherical** - longitude/latitude around the centroid, scaled by local radius. Same caveat.
- **LSCM** - real UV unwrap via `MeshBoolean::cgal::parameterize_lscm()` (CGAL's
`Surface_mesh_parameterization` package, LSCM algorithm). Computed **once per patch** (not
per-vertex like the others - it's a single global least-squares solve), then each vertex looks up
its precomputed UV. Requires the patch to be a single topological disk (one connected component,
one boundary loop) - `compute_lscm_uvs()` returns empty and the layer silently falls back to
Triplanar if not (e.g. multiple disconnected painted islands, or a fully closed patch).
CGAL's parameterizer needs a mesh with no isolated/unreferenced vertices, but `get_facets_strict()`
returns the *whole* mesh's vertex array - so there's a compaction step
(`compact_patch_with_map()`) that builds a clean sub-mesh + an index map back to the original
(uncompacted) vertex numbering, purely local to this file.
- **ViewProjected** ("From view") - a flat projection along a fixed direction captured from the 3D
camera, like a slide projector. `capture_view_projection()` takes the camera's right/up axes,
transforms them into the volume's *local* frame (so the projection rides along if the part is later
moved), and stores them as `TextureDisplacementLayer::view_project_right/up` (unit vectors, so the
projected coordinate stays in mm and `tiling_scale` keeps meaning mm). `sample_layer_height()`
projects `Vec2f(dot(pos, right), dot(pos, up))`. Single-valued per point, so - like LSCM but unlike
blended Triplanar - the fast preview and UV-check overlay precompute it per vertex
(`compute_layer_vertex_uvs()`) and drive the shader's `use_vertex_uv` path. Faces angled away from
the projector smear; that is inherent to view projection, not a bug.
Two companions to this mode:
- **Projection frame overlay** (`TextureProjectorFrame`, see below) - a semi-transparent window
dragged over the 3D view whose border becomes the projection's edge. Applying it stores an exact
**projective** map in `view_project_matrix`, which supersedes the affine `right`/`up` axes above
for that layer (`view_project_projective`).
- **"Project only on visible"** (`select_visible_faces()`) - repaints the layer with exactly the
facets the camera can see, so the projected area matches the viewpoint the projector was captured
from. Two tests: a facing test (normal vs. view direction, per triangle - under perspective the
view direction varies across the model, so it is taken from the eye to each centroid), then
`MeshRaycaster::get_unobscured_idxs()` on the survivors to drop facets hidden behind other
geometry, so a concave part's far inner wall is correctly excluded. One ray query per front-facing
facet, hence click-driven (on the checkbox and on each "Capture current view"), never per frame.
It **replaces** the layer's paint rather than adding to it - "project onto what I can see" would
otherwise accumulate every angle the user had ever looked from.
### Manual seams and island cutting
`TextureDisplacementLayer::lscm_seam_edges` - undirected mesh-vertex-index edge pairs the unwrap is
forced to cut along, on top of the dihedral-angle seams. `segment_into_charts()` takes a set of these
(translated from mesh → compacted-patch numbering inside `compute_patch_unwrap()`) and refuses to
union two triangles across a marked edge whatever their angle. Both the unwrap cache key and the
gizmo's `UVEditorState` include the seam list, so marking a seam (which leaves the paint mask
untouched) still forces a re-solve. Like the paint masks, seams are mesh-index-space and so dropped on
any topology change.
Two ways to write to it:
- **Mark seam (manual, #9)** - a "Mark seams" click mode (`m_seam_edit_mode`) that suppresses
painting. A click raycasts the volume (`m_c->raycaster()->raycasters()[idx]->unproject_on_mesh()`,
`idx` = the volume's slot among model-part volumes), finds the facet's edge nearest the hit point,
and toggles it. Marked edges render as a red overlay (`render_seam_overlay()`), pulled toward the
camera so they read on top. This is the Blender mark-seam workflow.
- **Cut island (auto, #17)** - `cut_island()` takes the selected chart's triangles (back-mapped from
the unwrap via `source_vertex`), finds their 3D bounding box, and marks every edge that straddles
the mid-plane perpendicular to the longest axis. The re-unwrap then splits the chart across its
narrow waist - the "islands might be very long" case. Exposed as the UV pane's **Cut** button.
### UV-check overlays (checker / distortion)
`resources/shaders/{110,140}/texture_displacement_uvcheck.{vs,fs}`, one shader with a `mode` uniform,
drawn over the painted patch (`rebuild_uvcheck_mesh()`/`render_uvcheck_mesh()`, P3N3T2: `normal.x` =
distortion, `tex_coord` = uv), pulled forward with a polygon offset. **Checker** (#13) samples a
procedural checkerboard at the layer's uv (per-vertex for LSCM/ViewProjected, in-shader triplanar
otherwise) - squares that stay square mean low distortion. **Distortion** (#14) colours each triangle
blue→green→red by `log2(uv_area / surface_area)` centred on the patch's *median* stretch (so a
globally-scaled unwrap reads as uniformly ideal and only relative stretch shows), averaged to
vertices. A separate **Show mesh wireframe** toggle (#8) draws the whole volume's triangle edges,
rebuilt only when the vertex count changes (not per stroke).
### Tiling
`DecodedHeightTexture::sample(uv, tile_enabled, tile_method)`. Two tile methods when enabled
(Repeat, MirroredRepeat). **When `tile_enabled` is false, sampling outside `[0,1)` returns `0`
directly** - clamping the *coordinate* into range (what an earlier version did) instead smears the
border row/column of pixels outward to infinity in every direction, which is a real bug that was
reported and fixed (visually: streaky lines radiating out from the painted patch).
### Subdivision — two modes
**Uniform (`subdivide_mesh_uniform()`)** — whole-mesh, 1-to-4 split. Recursive edge-midpoint split with
a shared per-pass midpoint cache (keyed by sorted vertex-index pair) so triangles sharing an edge get
the *same* new vertex - capped at `max_iterations` (default 6). Whole-mesh so it never leaves a
T-junction, at the cost of densifying everywhere. Wired as a "Subdivide steps" slider (**05**, 0 =
no subdivision), Apply snaps back to 0. Drops texture-displacement paint (no remap) via the standard
`save_painting()`/`set_mesh()`/`restore_painting()` dance; the other four channels are remapped.
**Adaptive (`subdivide_mesh_adaptive()`)** — refine **only the painted area**, by **Rivara longest-edge
bisection**. This is the algorithm that was "scoped out" originally for fear of the T-junction/crack
problem; it is safe because it is *conformal by construction*. Only **terminal** edges are ever bisected
- an edge that is the longest edge of *every* triangle sharing it - which splits both those triangles
along one shared midpoint at once, so a hanging node is never created. The edge to split for a triangle
that wants refining is found by **longest-edge propagation (LEPP)**: walk to the longest edge of
ever-longer-edged neighbours until a terminal one is reached, and bisect that. Edge length strictly
increases along the path (ties broken by mesh-vertex key, which both sides of an edge compute
identically), so the walk cannot cycle, and Rivara's result is that repeating it refines the original
triangle in a bounded number of bisections. The transition triangles it pulls in just outside the
painted patch are the graded band that makes the size change conformal.
The win: a small decal on a big model no longer quadruples the *whole* model's triangle count.
**Run to completion, worst-first, against a triangle budget.** The refinement loop is not a fixed number
of sweeps: it holds every triangle that is over its criteria in a max-heap keyed by *how many times over*
it is, pops the worst, walks its LEPP, bisects, and re-scores. Edge adjacency (`nb[e]`, the triangle
across each edge) is built **once** and maintained incrementally through each bisection, so the cost
scales with the refined region rather than with the whole model. `max_triangles` is the only bound;
stopping on it leaves a perfectly valid, still-conformal mesh that spent its budget on the largest errors.
This shape replaced a first version that ran a fixed 12 sweeps, each rebuilding a whole-mesh edge map and
bisecting one terminal edge per active triangle. Two failure modes came out of that, and they are worth
remembering because they look like separate bugs and are not: the sweeps were consumed grading the
*coarse surroundings* (whose edges are the longest, so they win every terminal-edge contest), which both
**stopped refinement of the painted patch far short** of the requested detail and left the band outside
it looking wildly over-refined relative to the patch itself.
**It carries the paint forward**, which is what makes it usable (uniform/remesh both drop paint). Because
the refinement is *driven by* the paint, the remap is trivial: `subdivide_mesh_adaptive()` fills an
`out_source[new_tri] = input_tri` map (children inherit their parent), and the gizmo rebuilds each
layer's mask on the new mesh - a new triangle is painted iff its source was fully painted in that
layer. `collect_paint_region()` derives both:
- the union refine-region: **exactly** the original triangles the brush touched, read straight off
`TriangleSplittingData::triangles_to_split` (`serialize()` records an entry per original triangle that
is either split - i.e. partially painted, the patch boundary - or carries a non-default state). No
dilation. An earlier version marked every triangle sharing a *vertex* with the patch, which drags in a
whole fan of huge unpainted neighbours and then refines *those* down to the resolution floor, since the
height field the detail test samples is not restricted to the painted area. The conformal closure
already grades the size change outward on its own; it does not need help.
- the per-layer fully-painted-triangle sets (a `get_facets_strict(ENFORCER)` sub-triangle with all three
*original* vertex indices == a whole, fully-painted original triangle; a partial stroke's sub-triangles
always carry a split vertex).
The other four channels still ride the normal `restore_painting()` remap. Covered by a conformality unit
test (`every_edge_used_twice` on a partially-refined cube - an exact crack detector for a closed mesh),
plus tests that the target edge length is actually *reached* and that the budget caps the result without
opening a crack.
Both share the gizmo's Preview/Apply/Done flow; the **"Only painted area (adaptive)"** checkbox picks
the mode, and the adaptive preview follows the paint live (`rebuild_preview()` refreshes the wireframe
while the subdivide preview is open in adaptive mode). The panel shows the previewed triangle count.
**Feature-adaptive (follow texture detail).** A sub-mode of adaptive (the **"Follow texture detail"**
checkbox) that puts triangles where the *displaced surface actually bends*, not evenly. The insight:
a flat region or a linear **ramp** needs no extra vertices (linear interpolation is exact for a ramp);
what needs them is **curvature** - the *second* derivative, not the gradient. So the extra predicate is a
**chord-error** test: sample the combined displacement at the triangle's three edge midpoints *and its
centroid* (sampling the interior is what catches a bump sitting inside a triangle, the blind spot of an
edge-only test) and take the largest departure from the flat triangle's barycentric interpolation. Refine
while that exceeds `chord_tolerance_mm` ("Detail (mm)"). Zero chord error on a ramp ⇒ untouched; high on
a bump/ridge/noise ⇒ refined until captured. Same conformal machinery, so still crack-free. The
per-triangle error is cached and recomputed only for the children of a split.
Four knobs bracket it, and all four matter:
- **"Max edge (mm)"** (`target_edge_length_mm`) is a **baseline that applies in feature mode too**.
Without it the chord test aliases: a big triangle over a fine pattern can sample four points that all
land at similar heights, report no error, and stall before refinement ever starts. The baseline
guarantees a sampling density fine enough for the curvature test to see the texture at all.
- **"Detail (mm)"** is the chord tolerance above.
- **"Min edge (mm)"** is a hard floor under both, and is what guarantees termination across a sharp
texture *step*, where the error never falls however fine the mesh gets.
- **"Added triangles (k)"** is the budget, passed as `max_triangles` (the model's own triangle count plus
the slider, so the control still means something on an already-dense model).
The height field is `make_combined_displacement_sampler()` - it mirrors `build_texture_displacement()`'s
per-layer setup (decode, patch centroid, cylinder axis, blend order, "lowest layer folds additively")
but evaluated per point. Two deliberate simplifications, both erring toward *more* detail (safe -
over-refinement is never a crack): every sampleable layer is sampled at every point (no per-point paint
test), and edge-smoothing falloff is ignored. Note the first one is *why* the refine region must not be
dilated - outside the paint the sampler still reports full relief. **LSCM layers are skipped** (no
per-point UV); a purely LSCM stack yields a null sampler and the code falls back to the length baseline
alone. Per-vertex heights are sampled lazily, so a small patch on a huge model never pays for the rest of
it. Covered by unit tests: a Gaussian bump refines densely at its center and leaves flat corners coarse,
a linear ramp produces *zero* extra triangles (the case a gradient criterion would over-refine), and a
flat field still honours the max-edge baseline.
### Fast bump preview (GPU-only, no CPU meshing)
`resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}`, registered as
`"texture_displacement_bump"`. Perturbs the *shading* normal from the height texture's local
gradient instead of moving geometry - active-layer-only, toggled via a "Fast preview (normal map)"
checkbox. Vertex format is `GLModel::Geometry::EVertexLayout::P3N3T2`: `normal.x` carries the
per-vertex paint weight (0/1) and `tex_coord` carries a precomputed texture UV, so it can use
`GLModel` normally instead of needing a hand-rolled VBO/VAO manager. Weight buffer is
rebuilt at the same cadence as the true-displacement preview (stroke-end/slider-release), using the
**live** `TriangleSelector` state (not the flushed model facets), so it doesn't lag by a full model
round-trip.
The perturbed normal is the analytic one for a height field `H = ±depth_mm · h(uv)` displaced along
`N` over any orthonormal surface tangent pair `T`/`B`:
N' = normalize(N (dH/da)·T (dH/db)·B), a = dot(p,T), b = dot(p,B)
The two slopes have to be genuine **mm-per-mm** derivatives for the preview's apparent depth to
match the bake's - see bug #13.
**Two projection paths (`use_vertex_uv` uniform):**
- **Triplanar (`use_vertex_uv = 0`)** - `uv` and the `T`/`B` axes are both derived in-shader from
the dominant normal component, mirroring `project_planar()`/`apply_uv_transform()`, and the slope is
formed analytically. `T`/`B` are the projection's axis-aligned pair, exact only when the face is
axis-aligned; the shader drops the along-normal component to keep the gradient in the surface. Here
one `uv` unit is exactly `tiling_scale` mm, so the `1/tiling_scale` gradient factor is right.
- **Precomputed UV (`use_vertex_uv = 1`, used for LSCM)** - `uv` comes per-vertex from the CPU
(`compute_lscm_uvs(patch, layer)`, so island placement + tiling/rotation/offset are already folded
in), and the perturbed normal is built with **Mikkelsen's method** ("Bump Mapping Unparametrized
Surfaces on the GPU"): the surface gradient taken directly from the screen-space derivatives of the
*sampled height* and position. **This makes no uv→mm scale assumption**, which is essential -
the first cut used the same global `1/tiling_scale` factor as triplanar and the depth came out
visibly wrong, because an LSCM map is **conformal, not isometric**: it is globally area-scaled but
the *local* mm-per-uv varies across the chart. `dFdx(h)` captures the true on-screen rate of change
however the chart is stretched. **This path is also what makes the fast preview follow the UV
editor: move an island and its uv - hence its bump - moves with it** (the bump mesh rebuilds on
drag-end, since `on_island_edited(finished)``rebuild_preview()``rebuild_bump_preview_mesh()`).
The branch is uniform (`use_vertex_uv` is a uniform) and the paint weight gates by multiply, so the
texture derivatives stay well defined. A triangle straddling a seam has a discontinuous uv → the
`det≈0` guard skips it (a localised preview-only artifact, never in the bake).
Remaining deliberate approximation: the GPU sampler's wrap mode stands in for
`tile_enabled`/`tile_method`, so with tiling *off* the GPU repeats where the CPU returns 0 outside
`[0,1)`.
### On-canvas "Adjust Texture" gizmo
A per-active-layer toggle ("Adjust placement (drag on model)") that disables painting and shows a
flat pan panel (free 2D drag on both axes) plus two arrows along the patch's own U/V axes
(constrained single-axis drag). Anchored to the painted patch's centroid/average-normal
(`compute_layer_paint_anchor()`). Hit-testing is screen-space distance/point-to-segment (not real
3D ray intersection against the handle geometry) - simple and good enough at this handle size.
### Projection frame overlay (ViewProjected)
`src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` - a semi-transparent, resizable `wxFrame` the user
drags **over the 3D view**, like a slide projector's gate. Whatever the model shows through it is what
the texture is projected onto, and the window's border becomes the hard edge of the displacement.
Press **Apply projection frame** and the gizmo reads the window's rectangle and commits it.
The window is deliberately **dumb**: it owns no placement state and reports nothing continuously. Its
position and size *are* the placement, read on demand at Apply - which is also when the expensive
visible-facet raycast runs. So dragging it is free and nothing recomputes until asked.
Plain 2D (`wxPaintDC`), not a `wxGLCanvas`: a second GL canvas would have to share the app's one real
`wxGLContext`, the cause of bugs #10 and #14 below. It only ever draws a bitmap and a border.
**The projective mapping (`apply_projection_frame()`)**. The frame defines a
**screen-space** rectangle, but the bake samples from a **local-space** position, so the two have to be
reconciled. `view_project_right/up` can only express an *affine* projection - exact under an
orthographic camera, but wrong under perspective, where the near end of a part projects larger than the
far end and no pair of axes reproduces that. So the layer instead stores a full projective map
(`view_project_matrix`, row-major 3×4, `uv = (row0·p̃/row2·p̃, row1·p̃/row2·p̃)`), built like this:
- `K = projection · view · (instance · volume)`, i.e. local → clip, the same product the renderer uses.
Note `Camera::get_projection_matrix()` is typed `Transform3d` (nominally affine) but its perspective
form explicitly writes a `(0, 0, 1, 0)` bottom row into the underlying 4×4, so `clip.w = z_eye` is
genuinely carried. The build therefore multiplies **`.matrix()` products** (plain `Matrix4d`), never
`Transform3d` products, which would not compose that row correctly.
- Window coordinates follow `igl::project`'s convention (as `CameraUtils::project` does), with y
measured downward. Writing `uv = (win rect_origin) / rect_size` makes u and v affine in
`ndc = clip.xyz / clip.w`; multiplying through by `clip.w` leaves a plain linear combination of `K`'s
rows, which is exactly the 3×4 matrix - the perspective divide survives intact.
- `w > 0` is checked rather than divided blindly. A point behind the projector has `w < 0` and divides
to a plausible-looking but **mirrored** uv - the classic way a projected decal reappears on the back
of a model. `project_uv_projective()` returns false there and the caller treats it as no height.
The map already includes placement, so `apply_uv_transform()` is **not** applied on top of it - the
window's own position and size are the placement, and the tiling/rotation/offset sliders would shove
the result off the frame the user just aligned. A "Clear" button drops back to the affine path where
those controls mean something again.
Apply also sets `tile_enabled = false`, so `DecodedHeightTexture::sample()` returns 0 outside `[0,1)`
and the border is a hard edge rather than the first seam of an endless repeat, and repaints the layer
via `select_visible_faces(&matrix)` - the frame's uv square clips the selection, which both matches the
paint to the border and keeps the ray queries proportional to the framed area instead of the model.
Owned by the gizmo and **destroyed** (not just hidden) in `on_shutdown()`. Closing it only hides it, so
reopening keeps it where it was left.
### UV Editor pane
`UVEditorCanvas` (`src/slic3r/GUI/UVEditorCanvas.hpp/.cpp`) - a standalone `wxGLCanvas` rendering the
flattened LSCM islands (per-island wireframe + outline + fill) over the height texture (background
quad tiled across the whole unwrap), with mouse pan/zoom. It is wrapped in a **`UVEditorPanel`**
(same file) that adds a button row (Frame / Snap / Average scale) and a status line
along the bottom naming the current gesture and the shortcuts in play. The *panel* is what is
registered as a `wxAuiPaneInfo` pane on `Plater`'s `m_aui_mgr`; `Plater::show_uv_editor(bool)`
shows/hides it (deferred via `CallAfter`, since the gizmo calls it mid-3D-frame), and
`get_uv_editor_canvas()` returns the inner canvas the gizmo talks to.
Deliberately **shares the app's one real `wxGLContext`** (`wxGetApp().init_glcontext(*this)`, the
same call `View3D`/`Preview`/`AssembleView` make) rather than creating an independent context like
`SkipPartCanvas` does elsewhere in this codebase - this is what lets it reuse the already-registered
`"flat"`/`"flat_texture"` shaders and `GLModel` as-is, instead of needing its own shader
compilation/VBO management.
**Geometry is uploaded once, in the unwrap's own (raw, mm) coordinates**, one `GLModel` set per
island; each island is then drawn through its own 2x3 affine (`island_transform_matrix()` composed
with the layer's tiling/rotation/offset) passed as the `flat` shader's `view_model_matrix`. A
drag updates one matrix per island and touches no vertex
buffer - `on_island_edited(!finished)` calls only `set_island_transforms()`, and the full
`set_islands()` rebuild happens solely when the unwrap itself changes (`unwrap_changed` in
`update_uv_editor()`).
**Gestures** (canvas-owned, reported to the gizmo as incremental deltas via `IslandEditFn`): left-drag
= move, right-drag or **R** = rotate (hold **Shift** to snap to 15° steps - quantised on the
*cumulative* rotation, not each delta, so it doesn't judder, and accumulated incrementally so it
survives crossing ±180°), **S** = scale (R/S modal, click/Enter to confirm, Esc to cancel), wheel =
zoom about the cursor, middle-drag = pan, **Home**/**F** = frame all. Scale writes
`TextureIsland::scale`; "Average scale" (`average_island_scales()`) sets every island to the mean, so
one island scaled by hand can be matched back to its neighbours' texel density. **Snap** (canvas-owned
`m_snap_enabled`, toggled from the toolbar) sticks a dragged island's nearest boundary vertex onto a
neighbouring island's at drag-*end* only - a magnet that re-applies mid-drag is very hard to pull out
of. Toolbar commands the canvas can't service itself (Average scale) are forwarded to the gizmo via
`CommandFn`; view-only ones (Frame, Snap) it handles directly.
## Known limitations / deferred work
- **No `.3mf` serialization** for texture-displacement paint data or texture assets. A background
agent attempted this in an earlier session, hit its own usage limit mid-edit, and left
`bbs_3mf.cpp` with an undefined forward-declared function; that partial edit was reverted rather
than shipped broken. Practical impact: **baked** geometry round-trips fine (it's just an ordinary
part of the mesh via the existing mesh serialization path) - what does *not* survive a project
save/reload is any *unbaked* paint stroke and texture layer definition.
- **No remap-across-topology-change** for texture-displacement paint (`ModelObject::split()`, mesh
boolean ops, Simplify, uniform subdivide, and remesh all drop it via `reset_extra_facets()`). The
other four paint channels (supported/seam/mmu/fuzzy) do get remapped in these cases. The lone
exception is **adaptive subdivide**, which carries texture-displacement paint forward itself via its
source map (see the Subdivision section) - a targeted remap that only works because the operation is
driven by the paint.
- **Cylindrical/Spherical axis/center are auto-picked heuristically**, not user-controllable - no
UI to override the auto-detected wrap axis if it picks the "wrong" one for an odd shape.
- **Fast preview covers the active layer only** — and is now the **default** view when the gizmo
opens (`m_use_bump_preview = true`): it is the instant, no-CPU-meshing preview, so it is the better
first impression while painting. The exact true-displacement view is one click away in the View row.
- **Displacement resolution is capped by the mesh's own vertex density.** Baking only ever *moves*
existing vertices (it never inserts any), so a coarse patch cannot show fine texture detail no
matter how high-resolution the height map is - that is what the subdivision controls are for
(uniform, adaptive, or feature-adaptive; see the Subdivision section). Since the rewrite the bake is
topology-preserving, so this is now a hard, explicit property rather than something partly papered
over by the old per-layer re-meshing.
## File map
**libslic3r (core, no GUI dependency):**
- `src/libslic3r/TextureDisplacement.hpp/.cpp` - data model, bake algorithm, projection methods,
tiling, subdivision (uniform + adaptive longest-edge bisection), post-process smoothing
(`smooth_mesh_vertices()`), and `TextureDisplacementOptions` (the whole-stack settings). See doc
comments throughout, they're kept accurate and up to date.
- `src/libslic3r/MeshBoolean.hpp/.cpp` - added `parameterize_lscm()` and `remesh_isotropic()`
in the `cgal` sub-namespace,
reusing the existing `CGALMesh`/`_EpicMesh`/conversion-helper infrastructure already there for
mesh boolean ops. New CGAL includes: `Polygon_mesh_processing/border.h`,
`Polygon_mesh_processing/connected_components.h`, `Surface_mesh_parameterization/{Error_code,
LSCM_parameterizer_3, parameterize}.h`. No new dependency - CGAL 5.6.3 is already vendored and
the `Surface_mesh_parameterization` package headers were already present, just unused before now.
- `src/libslic3r/Model.hpp/.cpp` - the 8 named `FacetsAnnotation` fields + accessor,
`texture_displacement_layers`, `texture_displacement_options`, and all the mirrored touch points
(see Data model above).
**GUI:**
- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp/.cpp` - the gizmo. Panel controls: dock/
undock toggle, brush/face/connected-area selection mode + "select whole model" button, per-layer
texture picker + depth/tiling/rotation/invert/tile-mode/projection-mode/blend-mode controls,
"Adjust placement" toggle (on-canvas gizmo), "Fast preview (normal map)" toggle, "Subdivide model"
button, Add layer/Erase all/Bake.
- `src/slic3r/GUI/TextureLibrary.hpp/.cpp` - scans the shipped + user texture folders, imports an
arbitrary image into the user folder (converting it to the 8-bit grayscale PNG libslic3r decodes),
and loads a library file's bytes for a layer. The image→grayscale-PNG conversion lives here, on the
GUI side, because libslic3r has no image toolkit; both the import path and the "pick a shipped
texture" path go through the same one function.
- `resources/textures/displacement/*.png` - the 10 shipped height maps (Bricks, Grid, Hexagons,
Knurl, Noise, Quilt, Studs, Waves, Weave, Wood Grain). All 512×512 8-bit grayscale and **seamless**
(each is periodic over the full image in both axes, so tiling shows no seam). Generated
procedurally; the whole `resources/` tree is installed recursively by CMake, so a new folder under
it ships with no build-system change.
- `src/slic3r/GUI/Jobs/TextureDisplacementBakeJob.hpp/.cpp` - background bake commit.
- `src/slic3r/GUI/Jobs/TextureDisplacementPreviewJob.hpp/.cpp` - background preview compute
(mirrors the bake job's shape but commits nothing to the Model).
- `src/slic3r/GUI/TextureProjectorFrame.hpp/.cpp` - the semi-transparent projection-frame overlay for
ViewProjected layers (plain 2D `wxPaintDC`, no GL context - see its section above).
- `src/slic3r/GUI/UVEditorCanvas.hpp/.cpp` - the 2D UV unwrap viewer widget.
- `src/slic3r/GUI/Plater.hpp/.cpp` - `uv_editor_canvas` member, AUI pane registration,
`get_uv_editor_canvas()`/`show_uv_editor()`.
- `src/slic3r/GUI/GLShadersManager.cpp` - registers `"texture_displacement_bump"`.
- `resources/shaders/{110,140}/texture_displacement_bump.{vs,fs}` - the bump-preview shader.
- `src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp` - `PainterGizmoType::TEXTURE_DISPLACEMENT`.
- `src/slic3r/GUI/Gizmos/GLGizmosManager.hpp/.cpp` - `EType::TextureDisplacement` registration.
- `src/slic3r/GUI/ImGuiWrapper.cpp` - the light-mode checkmark-color fix
**Tests:** `tests/libslic3r/test_texture_displacement.cpp` - **run and passing** (7 cases, 116
assertions). Covers `decode_height_texture` round-trip, empty-layer no-op, full-cube uniform
displacement, boundary-vertex pinning on a hand-built fan mesh, and - added with the bake rewrite -
a regression test that a **second layer over the same area actually contributes**,
a table-driven check of all four blend modes, and that the lowest layer ignores its
blend mode. `BUILD_TESTS` is `OFF` in the checked-in build cache; flip it on to run them:
cmake -S . -B build -DBUILD_TESTS=ON
cmake --build build --config Release --target libslic3r_tests -- -m
./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand

View File

@@ -1,326 +0,0 @@
# Texture Displacement — Feature & Controls Guide
Texture Displacement is a paint-style gizmo that stamps height-map textures onto a model's surface
and turns them into real relief — engraved or embossed detail — either as a live preview or baked
into actual mesh geometry. You paint where the texture applies, stack multiple textures as blended
layers, choose how each is projected onto the surface, and (for the unwrap projection) lay the result
out by hand in a dedicated 2D **UV Editor** pane.
This document describes every feature and control. For the internal architecture and algorithms, see
`TEXTURE_DISPLACEMENT.md`.
---
## Table of contents
1. [Quick start](#quick-start)
2. [Entering the tool](#entering-the-tool)
3. [Selection modes](#selection-modes)
4. [View modes](#view-modes)
5. [Auto update](#auto-update)
6. [Texture layers](#texture-layers)
7. [Per-layer settings](#per-layer-settings)
8. [Projection methods](#projection-methods)
9. [The UV Editor](#the-uv-editor)
10. [Seams](#seams)
11. [Adjust placement (on-model)](#adjust-placement-on-model)
12. [Preparing the mesh: Subdivide & Remesh](#preparing-the-mesh-subdivide--remesh)
13. [Baking & resetting](#baking--resetting)
14. [Controls reference](#controls-reference)
15. [Tips & limitations](#tips--limitations)
---
## Quick start
1. Select an object and open the **Texture displacement** gizmo from the left toolbar.
2. A texture layer is added automatically. Pick a texture from the layer's picker, or import your own.
3. **Paint** the area you want the texture to affect (or press **Select whole model**).
4. The relief appears live on the model. Tune **Depth**, **Tile size**, **Rotation**, etc.
5. If the model is low-poly, use **Subdivide** or **Remesh** so there are enough vertices for detail.
6. Press **Bake** to convert the preview into real geometry, or leave it as a live preview.
> The tool only ever affects the **painted** area. Everything you don't paint keeps its original
> surface, and bake blends the relief seamlessly into it.
---
## Entering the tool
The gizmo lives on the left gizmo toolbar (icon: `toolbar_texture_displacement.svg`). Its settings
panel opens beside the toolbar. You can **Dock panel / Undock panel** (top of the panel) to pin it or
float it freely over the 3D view, and **Close** at the bottom exits the gizmo.
When you first open the tool on a never-textured object it starts with **one texture layer already
added**, so you can paint straight away.
---
## Selection modes
Choose *how* you paint. All three write into the **active layer's** mask.
| Mode | What it does |
|------|--------------|
| **Brush** | Free-hand painting with a round brush. Shows a **Brush size** slider and a **Circle / Sphere** choice (circle = surface disc, sphere = 3D ball that also paints around curves). |
| **Face** | Click a single triangle to paint it. |
| **Connected area** | Click to flood-fill a region; the **Angle threshold** slider limits how far the fill spreads across changes in surface angle. |
- **Select whole model** — marks the entire model as painted for the active layer, instead of
brushing it by hand.
---
## View modes
A row of icon buttons labelled **View** controls how the painted area is shown. The first four are a
radio group; **Wireframe** is an independent toggle. Hover any icon for its tooltip.
| View | Meaning |
|------|---------|
| **Normal** | The true displaced geometry — exactly what **Bake** produces. Rebuilt in the background. |
| **Fast** | A GPU bump-shaded approximation of the *active layer only*. No real geometry movement — quick to update, not exact. Best while tuning or dragging islands. |
| **Checker** | A test grid painted over the unwrap so you can see stretching (squares stay square where the map isn't distorted). |
| **Distortion** | A blue→green→red heatmap of how much each area is compressed or stretched in UV space. Needs the **Unwrap (LSCM)** projection. |
| **Wireframe** | Overlays the mesh edges (white). Independent of the view above; in **Normal** view it sits on the displaced surface. |
---
## Auto update
**Auto update** (on by default) rebuilds the true displaced geometry as soon as *anything* changes —
painting, swapping textures, moving sliders. Turn it off on very heavy models to only rebuild when you
release a slider (painting still updates on stroke end).
---
## Texture layers
You can stack up to **8** texture layers. Each has its own independent paint mask, its own texture,
and its own parameters, and they combine in slot order like layers in an image editor.
- **Add a layer** — the ** icon** to the right of the *Texture layers* heading (reuses the tool icon
for now).
- **Remove** — the button on each layer's header row.
- **Active layer** — click a layer's header (or anywhere in its block) to make it active. The active
layer is the one you paint into and the one whose block is tinted. Only one layer is active at a time.
- **Erase all** — clears the active layer's paint.
Each layer shows a texture **picker** (large preview + name). Open it to choose from the shipped
library or import your own image (any png/jpg/bmp; it's converted to an 8-bit grayscale height map and
copied into your user texture folder so app updates can't overwrite it).
---
## Per-layer settings
| Control | Range / options | What it does |
|---------|-----------------|--------------|
| **Depth (mm)** | 0.0110 (log) | Maximum displacement along the surface normal. |
| **Tile size (mm)** | 0.2200 (log) | Physical size of one texture tile on the surface. |
| **Rotation** | 0360° | Rotates the texture on the surface. |
| **Midlevel** | 010 | The grey level that means "don't move". At 0 the texture only pushes outward; raise it and darker texels cut *inward* (one map both embosses and engraves). 0.5 makes mid-grey neutral. |
| **Smoothing** | 01 | Blurs the height texture before it displaces — rounds hard edges and removes speckle without needing a softer source image. |
| **Edge smoothing** | checkbox + **Edge amount** 01 | Fades the relief to flat toward the *edge of the painted area*, so it blends into the surrounding surface. A small amount softens only a thin band at the very edge; the maximum flattens the whole painted face. |
| **Invert** | checkbox | Flips the height map (peaks become valleys). |
| **Blend** | Add / Subtract / Multiply / Divide | How this layer combines with the layers **below** it where they overlap. Add/Subtract pile relief on or carve it away; Multiply/Divide scale the relief underneath (a mask). The lowest painted layer is the **Base** and always behaves additively. |
| **Tile** | checkbox + **Repeat / Mirrored repeat** | When off, the texture is placed once (a decal) instead of repeating. Mirrored repeat flips every other tile to hide seams. |
| **Projection** | see below | How the texture is mapped onto the painted surface. |
> **Midlevel warning:** cutting inward can fold the surface through itself in sharp concave corners or
> thin walls. Keep Depth small relative to the feature you're cutting into; the panel warns when a deep
> inward setting is risky.
---
## Projection methods
How the 2D texture is wrapped onto the 3D painted area.
| Method | Best for | Notes |
|--------|----------|-------|
| **Triplanar (blended)** | Patches wrapping around edges | Projects from all three axes at once and blends, so there's no seam across a sharp edge. |
| **Cylindrical** | Round, tube-like selections | Wraps the texture around the patch's own centre/axis. |
| **Spherical** | Ball-like selections | Longitude/latitude wrap around the patch centre. |
| **Unwrap (LSCM)** | Flat, controlled layout | A real conformal unwrap. Cuts the area into pieces at sharp edges (see **Seam angle**), flattens each, and lets you lay them out by hand in the **UV Editor**. Unlocks Checker/Distortion, seams, and island editing. |
| **From view** | Decals / slide-projector look | Projects straight onto the surface from the current camera direction. Use **Capture current view** to re-lay it from wherever you're looking. |
### LSCM-only controls
These appear when a layer uses **Unwrap (LSCM)**:
- **Seam angle** (590°) — edges sharper than this are cut so each piece lies flat. Lower cuts more
(less stretching, more seams); raise to keep more in one piece. A box's 90° corners are cut by
default. *Ignored once you've marked any seam by hand* (your seams then define the pieces).
- **Connect islands** (on by default) — lays the unwrap out as a **connected net**: pieces that share
an edge are unfolded next to each other (a cube becomes a joined net instead of six loose squares).
They stay separate islands, so you can still move any of them by hand. Turn off for the classic
packed-grid layout.
- **Open UV editor** — shows the flattened unwrap in a side pane (see below). Opens *only* when you
turn this on — it never pops up on its own.
- **Mark seams** / **Path** / **Clear seams** — see [Seams](#seams).
- An **Unwrap: N islands, F faces, V verts** read-out tells you what the unwrap actually produced.
---
## The UV Editor
A dockable 2D pane (enable **Open UV editor** on an LSCM layer) showing the flattened unwrap over the
height texture. Islands are the flattened pieces; you can rearrange them freely — nothing re-packs them
behind your back. Moving an island updates the model **live** (in Fast view it tracks the cursor
smoothly, via a shader uniform — no rebuild until you release).
### Navigation
| Action | Control |
|--------|---------|
| Pan | Middle-drag |
| Zoom | Mouse wheel (zooms about the cursor) |
| Frame everything | **Home** or **F**, or the **Frame** toolbar button |
### Editing an island
| Action | Control |
|--------|---------|
| Select | Left-click an island |
| Move | Left-drag |
| Rotate | Right-drag, or press **R** then move the mouse (click/Enter to confirm, Esc to cancel) |
| Rotate snapped | Hold **Shift** while rotating — snaps to **global** 15° marks (0/15/30…). A protractor dial with tick marks and the current angle is shown. |
| Scale | Press **S** then move the mouse (click/Enter to confirm, Esc to cancel) |
| Undo / Redo | **Ctrl+Z** / **Ctrl+Shift+Z** or **Ctrl+Y** |
The **selected** island gets a bold light-green outline and a brighter wireframe; unselected islands
are a translucent light-green wash. The texture underneath repeats exactly as it will when baked.
A **status line** along the bottom always names the current gesture and the shortcuts in play.
### Toolbar
| Button | Action |
|--------|--------|
| **Frame** | Frame all islands (same as Home). |
| **Snap** | Toggle magnetic snapping — a dragged island sticks its boundary to a neighbour's when they come close. |
| **Avg scale** | Give every island the same texel density (Blender's "Average Islands Scale"). |
| **Cut** | Split the selected island across its long axis (useful for very long islands). |
| **Join** | Unfold the selected island onto its nearest neighbour along their shared edge — keeps both as separate islands with their own borders. |
| **Unjoin** | Send the selected island back to its own packed position. |
> **Checker / Distortion in the UV editor:** selecting those View modes also colours the UV pane — a
> checker background, or a per-island distortion heatmap — so you can judge stretch in 2D as well as
> on the model.
---
## Seams
Seams are edges the unwrap is forced to cut along, on top of whatever the Seam angle cuts — the
Blender "mark seam" workflow. They let you control exactly where the unwrap splits.
Enable **Mark seams** on an active LSCM layer, then:
- **Click an edge** on the model to mark it (it turns **red**); click a red edge again to unmark it.
The edge under the cursor is highlighted **yellow** so you can see what a click will toggle.
- **Path mode** (the **Path** checkbox) — for dense meshes where clicking each edge is tedious: click a
start point, then an end point, and the whole **shortest path** between them is seamed at once. It
chains (each click extends from the last point); the start vertex is shown in **green**.
- **Ctrl+drag** rotates/pans the camera while in seam mode.
- **Clear seams** removes them all.
Once any seam is marked, the automatic Seam-angle cutting is disabled so *your* seams define the
islands — pieces you leave un-seamed merge together.
---
## Adjust placement (on-model)
**Adjust placement** (on an active layer) lets you position the texture by dragging a handle on the
model instead of nudging the Rotation/offset numbers. The handle is a flat panel in the patch's
tangent plane (drag anywhere on it to move freely) plus U/V arrows for single-axis nudges. It's
anchored to the painted patch, so paint something first.
---
## Preparing the mesh: Subdivide & Remesh
Displacement can only move vertices that exist, so a coarse model needs more of them first.
### Subdivide
Splits every triangle into four, **15 times** (each step roughly quadruples the triangle count).
- **Subdivide steps** (15) — how many times to split.
- **Preview subdivision** — shows the result as a **cyan wireframe** without changing the model.
- **Apply** — commits the subdivision to the geometry.
- **Done** — ends the preview and leaves the model as it is.
### Remesh
Rebuilds the whole model with triangles close to a target edge length — evens out a mesh with wildly
varying triangle sizes (CGAL isotropic remeshing).
- **Target edge (mm)** — desired triangle edge length (seeded to the model's current average).
- **Remesh** — splits the big triangles and merges the small ones to that size.
> Both Subdivide-Apply and Remesh **replace the geometry** and clear any *not-yet-baked* paint on it
> (already-baked relief is kept). If you had the mesh **Wireframe** on before, it stays on afterward.
---
## Baking & resetting
- **Bake** — converts the current preview into real, permanent mesh geometry, restricted to the
painted area. Runs in the background; the button shows *Baking…* while it works.
- **Erase all** — clears the active layer's paint.
Baking is the exact same algorithm as the **Normal** preview, so what you see is what you get.
---
## Controls reference
### Mouse — 3D view (while painting)
| Input | Action |
|-------|--------|
| Left-drag | Paint the active layer |
| Ctrl + drag | Rotate / pan the camera (works in seam mode too) |
| Wheel | Zoom |
### Mouse & keys — UV Editor
| Input | Action |
|-------|--------|
| Left-click | Select island |
| Left-drag | Move island |
| Right-drag | Rotate island |
| **R** / **S** | Modal rotate / scale (mouse drives it, click or Enter confirms, Esc cancels) |
| **Shift** (while rotating) | Snap to global 15° marks |
| Middle-drag | Pan |
| Wheel | Zoom about cursor |
| **Home** / **F** | Frame all islands |
| **Ctrl+Z** / **Ctrl+Shift+Z** / **Ctrl+Y** | Undo / redo |
### Seam mode
| Input | Action |
|-------|--------|
| Click edge | Mark / unmark a seam (yellow = hover, red = marked) |
| Click (Path mode) | Set start, then seam the shortest path to the next click |
| Ctrl + drag | Rotate / pan camera |
---
## Tips & limitations
- **Paint first, then bake.** The preview is free to explore; only Bake changes the real mesh.
- **Not enough detail?** Subdivide or Remesh before painting fine textures.
- **Inward cuts** (high Midlevel + big Depth) can self-intersect on thin walls or sharp concave
corners — keep Depth modest there.
- **Fast vs Normal:** Fast preview shades a bump and shows only the active layer; use it for quick
tuning and smooth UV dragging, but trust **Normal**/**Bake** for the exact result.
- **Topology changes drop unbaked paint.** Subdivide-Apply, Remesh, and Simplify replace the mesh, and
texture-displacement paint isn't remapped across that change (already-baked relief is unaffected).
- **Island placements** are tied to the current unwrap. Re-painting or changing the Seam angle can
re-segment the charts and renumber them, so a re-unwrap re-lays the connected net and discards
hand placements made before it.
- **Connect islands** is on by default; turn it off (per layer) for the classic packed-grid layout, or
if an unfold looks wrong on an unusual mesh.

View File

@@ -128,6 +128,10 @@ cmake_minimum_required(VERSION 3.13)
if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
# Re-set after cmake_minimum_required above cleared it; use BoostConfig, not the removed FindBoost.
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
if(OpenVDB_FIND_QUIETLY)
set (_quiet "QUIET")

2
deps/CMakeLists.txt vendored
View File

@@ -423,6 +423,7 @@ endif ()
include(OCCT/OCCT.cmake)
include(OpenCV/OpenCV.cmake)
include(python3/python3.cmake)
include(wxInspector/wxInspector.cmake)
set(_dep_list
dep_Boost
@@ -446,6 +447,7 @@ set(_dep_list
${EXPAT_PKG}
dep_libnoise
dep_python3
dep_wxInspector
)
if (MSVC)

13
deps/wxInspector/wxInspector.cmake vendored Normal file
View File

@@ -0,0 +1,13 @@
orcaslicer_add_cmake_project(
wxInspector
URL https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip
URL_HASH SHA256=0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7
DEPENDS ${WXWIDGETS_PKG}
CMAKE_ARGS
-DCMAKE_CXX_FLAGS="-DwxDEBUG_LEVEL=0"
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
if (MSVC)
add_debug_dep(dep_wxInspector)
endif ()

View File

@@ -26,6 +26,7 @@ orcaslicer_add_cmake_project(
GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets"
GIT_TAG v3.3.2
GIT_SHALLOW ON
GIT_SUBMODULES 3rdparty/catch 3rdparty/pcre 3rdparty/libwebp
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG}
CMAKE_ARGS
-DwxBUILD_PRECOMP=ON

View File

@@ -0,0 +1,111 @@
# Move `wxInspectable` into `DPIAware` — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Move `wxInspector::wxInspectable` from individual leaf classes into the common `DPIAware<P>` template so every DPIAware widget is automatically inspectable and gets the inspector keyboard shortcut.
**Architecture:** `DPIAware<P>` gains `wxInspector::wxInspectable` as a second base class and calls `SetupInspectorAccelerator(this)` in its constructor. `DPIDialog` and `MainFrame` drop their now-redundant `wxInspectable` inheritance and `SetupInspectorAccelerator` calls.
**Tech Stack:** C++17, wxWidgets, wxInspector
## Global Constraints
- Build with `D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe`
- Use `--config RelWithDebInfo` for all builds
- Cross-platform: must compile on Windows, macOS, and Linux
- Match existing code style: PascalCase classes, `#pragma once`
- Do NOT commit files under `.superpowers/`
- Do NOT commit `task.md`
---
### Task 1: Move `wxInspectable` and `SetupInspectorAccelerator` into `DPIAware<P>`
**Files:**
- Modify: `src/slic3r/GUI/GUI_Utils.hpp:92` (DPIAware template — add wxInspectable base + SetupInspectorAccelerator call)
- Modify: `src/slic3r/GUI/GUI_Utils.hpp:276` (DPIDialog — drop wxInspectable + SetupInspectorAccelerator)
- Modify: `src/slic3r/GUI/MainFrame.hpp:96` (MainFrame — drop wxInspectable)
- Modify: `src/slic3r/GUI/MainFrame.cpp:304` (MainFrame constructor — drop SetupInspectorAccelerator)
**Interfaces:**
- Consumes: Nothing (standalone refactor)
- Produces: All DPIAware widgets automatically inherit `wxInspector::wxInspectable` and get Ctrl+Shift+I accelerator
- [ ] **Step 1: Add `wxInspectable` to `DPIAware<P>` and call `SetupInspectorAccelerator`**
In `src/slic3r/GUI/GUI_Utils.hpp`, line 92, change the base class:
```cpp
// Before:
template<class P> class DPIAware : public P
// After:
template<class P> class DPIAware : public P, public wxInspector::wxInspectable
```
In the constructor body of `DPIAware<P>`, after `this->CenterOnParent();` (currently line 110), add:
```cpp
SetupInspectorAccelerator(this);
```
(`<wx/inspector/inspector.h>` is already included at line 23.)
- [ ] **Step 2: Remove redundant `wxInspectable` and `SetupInspectorAccelerator` from `DPIDialog`**
In `src/slic3r/GUI/GUI_Utils.hpp`, line 276, change:
```cpp
// Before:
class DPIDialog : public DPIAware<wxDialog>, public wxInspector::wxInspectable
// After:
class DPIDialog : public DPIAware<wxDialog>
```
In the `DPIDialog` constructor body, remove the `SetupInspectorAccelerator(this);` line (currently line 286). The rest of the constructor stays.
- [ ] **Step 3: Remove redundant `wxInspectable` from `MainFrame`**
In `src/slic3r/GUI/MainFrame.hpp`, line 96, change:
```cpp
// Before:
class MainFrame : public DPIFrame, public wxInspector::wxInspectable
// After:
class MainFrame : public DPIFrame
```
`MainFrame` now gets `wxInspectable` through `DPIFrame``DPIAware<wxFrame>`.
- [ ] **Step 4: Remove redundant `SetupInspectorAccelerator` from `MainFrame` constructor**
In `src/slic3r/GUI/MainFrame.cpp`, line 304, remove the line:
```cpp
SetupInspectorAccelerator(this);
```
It is now called automatically by the `DPIAware<wxFrame>` constructor.
- [ ] **Step 5: Build to verify compilation**
```powershell
$cmakePath = "D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe"
& $cmakePath --build . --config RelWithDebInfo --target ALL_BUILD -- -m
```
Expected: Build succeeds with zero new errors or warnings.
- [ ] **Step 6: Commit**
```bash
git add src/slic3r/GUI/GUI_Utils.hpp src/slic3r/GUI/MainFrame.hpp src/slic3r/GUI/MainFrame.cpp
git commit -m "refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware
DPIAware<P> now inherits wxInspector::wxInspectable and calls
SetupInspectorAccelerator in its constructor, making all DPIAware
widgets automatically appear in the inspector tree with the
Ctrl+Shift+I shortcut. Remove redundant wxInspectable inheritance
and SetupInspectorAccelerator calls from DPIDialog and MainFrame.
Co-Authored-By: Claude <noreply@anthropic.com>"
```

View File

@@ -0,0 +1,753 @@
# wxInspector Plugins for OrcaSlicer — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build two wxInspector plugins (DPIAware + CustomWidgets) that expose OrcaSlicer custom control properties in the inspector's property grid.
**Architecture:** Two plugins in a shared folder under `src/slic3r/Utils/wxInspectorPlugins/`. DPIAwarePlugin uses `dynamic_cast<DPIFrame*>/<DPIDialog*>` for detection; CustomWidgetsPlugin uses per-type `dynamic_cast`. Both registered as static singletons via a single inline function in `Registration.hpp`, called from `MainFrame` constructor.
**Tech Stack:** C++17, wxWidgets, wxInspector plugin API (`wx/inspector/plugin.h`, `wx/inspector/inspector.h`), OrcaSlicer custom widget headers
## Global Constraints
- Plugins placed under `src/slic3r/Utils/wxInspectorPlugins/`
- Build with `D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe`
- Minimal source changes: only trivial (one-line) getters/setters added to existing classes
- Cross-platform: must compile on Windows, macOS, and Linux
- Match existing code style: PascalCase classes, snake_case functions, `#pragma once`
---
### Task 1: Add getters/setters to existing Orca widget headers
**Files:**
- Modify: `src/slic3r/GUI/GUI_Utils.hpp` (DPIAware template — add 4 methods)
- Modify: `src/slic3r/GUI/Widgets/Button.hpp` (add 3 getters)
- Modify: `src/slic3r/GUI/Widgets/CheckBox.hpp` (add 1 getter)
- Modify: `src/slic3r/GUI/Widgets/TextInput.hpp` (add 1 getter)
- Modify: `src/slic3r/GUI/Widgets/LabeledStaticBox.hpp` (add 4 getter declarations)
- Modify: `src/slic3r/GUI/Widgets/LabeledStaticBox.cpp` (add 4 getter implementations)
**Interfaces:**
- Consumes: Nothing (prerequisite for all other tasks)
- Produces:
- `DPIAware<P>::set_scale_factor(float)`, `DPIAware<P>::set_prev_scale_factor(float)`, `DPIAware<P>::set_em_unit(int)`, `DPIAware<P>::force_rescale() const`
- `Button::GetStyle()`, `Button::GetType()`, `Button::IsSelected()`
- `CheckBox::IsHalfChecked()`
- `TextInput::GetCornerRadius()`
- `LabeledStaticBox::GetCornerRadius()`, `LabeledStaticBox::GetBorderWidth()`, `LabeledStaticBox::GetBorderColor()`, `LabeledStaticBox::GetScale()`
- [ ] **Step 1: Add DPIAware setters/getter in GUI_Utils.hpp**
After line 184 (`float prev_scale_factor() const { return m_prev_scale_factor; }`), add:
```cpp
void set_scale_factor(float v) { m_scale_factor = v; }
void set_prev_scale_factor(float v) { m_prev_scale_factor = v; }
void set_em_unit(int v) { m_em_unit = v; }
bool force_rescale() const { return m_force_rescale; }
```
- [ ] **Step 2: Add Button getters in Button.hpp**
After line 79 (`void SetSelected(bool selected = true) { m_selected = selected; }`), add:
```cpp
ButtonStyle GetStyle() const { return m_style; }
ButtonType GetType() const { return m_type; }
bool IsSelected() const { return m_selected; }
```
- [ ] **Step 3: Add CheckBox getter in CheckBox.hpp**
After line 16 (`void SetHalfChecked(bool value = true);`), add:
```cpp
bool IsHalfChecked() const { return m_half_checked; }
```
- [ ] **Step 4: Add TextInput getter in TextInput.hpp**
After line 44 (`void SetCornerRadius(double radius);`), add:
```cpp
int GetCornerRadius() const { return static_cast<int>(radius); }
```
(Note: `radius` is inherited from `StaticBox` which has it as a protected `double` member.)
- [ ] **Step 5: Add LabeledStaticBox getter declarations in LabeledStaticBox.hpp**
After line 46 (`bool Enable(bool enable) override;`), add:
```cpp
int GetCornerRadius() const { return m_radius; }
int GetBorderWidth() const { return m_border_width; }
StateColor GetBorderColor() const { return border_color; }
float GetScale() const { return m_scale; }
```
(Note: all of `m_radius`, `m_border_width`, `border_color`, `m_scale` are protected members, accessible to inline methods.)
- [ ] **Step 6: Commit**
```bash
git add src/slic3r/GUI/GUI_Utils.hpp src/slic3r/GUI/Widgets/Button.hpp src/slic3r/GUI/Widgets/CheckBox.hpp src/slic3r/GUI/Widgets/TextInput.hpp src/slic3r/GUI/Widgets/LabeledStaticBox.hpp
git commit -m "feat: add getters/setters for wxInspector plugin access
Add minimal public accessors to DPIAware (set_scale_factor,
set_prev_scale_factor, set_em_unit, force_rescale), Button
(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked),
TextInput (GetCornerRadius), and LabeledStaticBox
(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale)."
```
---
### Task 2: Create Registration helper header
**Files:**
- Create: `src/slic3r/Utils/wxInspectorPlugins/Registration.hpp`
**Interfaces:**
- Consumes: Nothing (forward-declares plugin classes)
- Produces: `RegisterOrcaInspectorPlugins()`
- [ ] **Step 1: Create directory**
```bash
mkdir -p src/slic3r/Utils/wxInspectorPlugins
```
- [ ] **Step 2: Write Registration.hpp**
```cpp
#pragma once
namespace wxInspector {
class wxInspectorPlugin;
void RegisterPlugin(wxInspectorPlugin* plugin);
}
// Forward declare our plugins
class DPIAwarePlugin;
class CustomWidgetsPlugin;
inline void RegisterOrcaInspectorPlugins()
{
static DPIAwarePlugin dpiaware;
static CustomWidgetsPlugin customWidgets;
wxInspector::RegisterPlugin(&dpiaware);
wxInspector::RegisterPlugin(&customWidgets);
}
```
- [ ] **Step 3: Commit**
```bash
git add src/slic3r/Utils/wxInspectorPlugins/Registration.hpp
git commit -m "feat: add wxInspector plugin registration helper
Add RegisterOrcaInspectorPlugins() inline function that creates
and registers the DPIAwarePlugin and CustomWidgetsPlugin as
static instances (matching wxInspector's built-in pattern)."
```
---
### Task 3: Create DPIAwarePlugin
**Files:**
- Create: `src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp`
- Create: `src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp`
**Interfaces:**
- Consumes: Task 1 (DPIAware getters/setters), Task 2 (registration pattern)
- Produces: `class DPIAwarePlugin : public wxInspector::wxInspectorPlugin`
- [ ] **Step 1: Write DPIAwarePlugin.hpp**
```cpp
#pragma once
#include <wx/inspector/plugin.h>
class DPIAwarePlugin : public wxInspector::wxInspectorPlugin
{
public:
wxString GetName() const override;
bool CanProvideProperties(wxClassInfo* info) override;
wxVector<wxInspector::PropertyDef> GetProperties(
wxInspector::InspectableObject& obj) override;
};
```
- [ ] **Step 2: Write DPIAwarePlugin.cpp**
```cpp
#include "DPIAwarePlugin.hpp"
#include "slic3r/GUI/GUI_Utils.hpp" // DPIFrame, DPIDialog, DPIAware<P>
#include <wx/window.h>
namespace {
template<typename T>
void addDPIProps(T* dpi, wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Scale Factor", "DPI Scaling", PropertyType::String,
wxString::Format("%.2f", dpi->scale_factor()), false, {},
[dpi]() { return wxString::Format("%.2f", dpi->scale_factor()); },
[dpi](const wxString& v) {
double val;
if (wxSscanf(v, "%lf", &val) != 1) return false;
dpi->set_scale_factor((float) val);
return true;
}});
props.push_back({"Prev Scale Factor", "DPI Scaling", PropertyType::String,
wxString::Format("%.2f", dpi->prev_scale_factor()), false, {},
[dpi]() { return wxString::Format("%.2f", dpi->prev_scale_factor()); },
[dpi](const wxString& v) {
double val;
if (wxSscanf(v, "%lf", &val) != 1) return false;
dpi->set_prev_scale_factor((float) val);
return true;
}});
props.push_back({"EM Unit", "DPI Scaling", PropertyType::Integer,
wxString::Format("%d", dpi->em_unit()), false, {},
[dpi]() { return wxString::Format("%d", dpi->em_unit()); },
[dpi](const wxString& v) {
long val;
if (!v.ToLong(&val)) return false;
dpi->set_em_unit((int) val);
return true;
}});
props.push_back({"Normal Font", "DPI Scaling", PropertyType::ReadOnly,
dpi->normal_font().GetNativeFontInfoDesc(), true, {},
[dpi]() { return dpi->normal_font().GetNativeFontInfoDesc(); },
nullptr});
props.push_back({"Force Rescale", "DPI Scaling", PropertyType::Boolean,
dpi->force_rescale() ? "true" : "false", true, {},
[dpi]() { return dpi->force_rescale() ? "true" : "false"; },
nullptr});
}
} // anonymous namespace
wxString DPIAwarePlugin::GetName() const
{
return "OrcaDPIAware";
}
bool DPIAwarePlugin::CanProvideProperties(wxClassInfo* info)
{
return info->IsKindOf(CLASSINFO(wxWindow));
}
wxVector<wxInspector::PropertyDef> DPIAwarePlugin::GetProperties(
wxInspector::InspectableObject& obj)
{
wxVector<wxInspector::PropertyDef> props;
wxWindow* win = obj.AsWindow();
if (!win) return props;
if (auto* frame = dynamic_cast<DPIFrame*>(win)) {
addDPIProps(frame, props);
} else if (auto* dlg = dynamic_cast<DPIDialog*>(win)) {
addDPIProps(dlg, props);
}
return props;
}
```
- [ ] **Step 3: Commit**
```bash
git add src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp
git commit -m "feat: add DPIAware wxInspector plugin
Exposes DPI scaling properties (scale_factor, prev_scale_factor,
em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog
widgets. Uses dynamic_cast for detection and a template helper
to capture the correct static type for lambda accessors."
```
---
### Task 4: Create CustomWidgetsPlugin
**Files:**
- Create: `src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp`
- Create: `src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp`
**Interfaces:**
- Consumes: Task 1 (all widget getters), Task 2 (registration pattern)
- Produces: `class CustomWidgetsPlugin : public wxInspector::wxInspectorPlugin`
- [ ] **Step 1: Write CustomWidgetsPlugin.hpp**
```cpp
#pragma once
#include <wx/inspector/plugin.h>
class CustomWidgetsPlugin : public wxInspector::wxInspectorPlugin
{
public:
wxString GetName() const override;
bool CanProvideProperties(wxClassInfo* info) override;
wxVector<wxInspector::PropertyDef> GetProperties(
wxInspector::InspectableObject& obj) override;
private:
void addButtonProps(class Button* btn,
wxVector<wxInspector::PropertyDef>& props);
void addCheckBoxProps(class CheckBox* cb,
wxVector<wxInspector::PropertyDef>& props);
void addTextInputProps(class TextInput* ti,
wxVector<wxInspector::PropertyDef>& props);
void addSwitchButtonProps(class SwitchButton* sb,
wxVector<wxInspector::PropertyDef>& props);
void addProgressBarProps(class ProgressBar* pb,
wxVector<wxInspector::PropertyDef>& props);
void addLabelProps(class Label* lbl,
wxVector<wxInspector::PropertyDef>& props);
void addLabeledStaticBoxProps(class LabeledStaticBox* lsb,
wxVector<wxInspector::PropertyDef>& props);
};
```
- [ ] **Step 2: Write CustomWidgetsPlugin.cpp — includes and GetName/CanProvideProperties**
```cpp
#include "CustomWidgetsPlugin.hpp"
#include "slic3r/GUI/Widgets/Button.hpp"
#include "slic3r/GUI/Widgets/CheckBox.hpp"
#include "slic3r/GUI/Widgets/TextInput.hpp"
#include "slic3r/GUI/Widgets/SwitchButton.hpp"
#include "slic3r/GUI/Widgets/ProgressBar.hpp"
#include "slic3r/GUI/Widgets/Label.hpp"
#include "slic3r/GUI/Widgets/LabeledStaticBox.hpp"
#include <wx/window.h>
#include <wx/tglbtn.h>
wxString CustomWidgetsPlugin::GetName() const
{
return "OrcaCustomWidgets";
}
bool CustomWidgetsPlugin::CanProvideProperties(wxClassInfo* info)
{
return info->IsKindOf(CLASSINFO(wxWindow));
}
wxVector<wxInspector::PropertyDef> CustomWidgetsPlugin::GetProperties(
wxInspector::InspectableObject& obj)
{
wxVector<wxInspector::PropertyDef> props;
wxWindow* win = obj.AsWindow();
if (!win) return props;
if (auto* btn = dynamic_cast<Button*>(win))
addButtonProps(btn, props);
if (auto* cb = dynamic_cast<CheckBox*>(win))
addCheckBoxProps(cb, props);
if (auto* ti = dynamic_cast<TextInput*>(win))
addTextInputProps(ti, props);
if (auto* sb = dynamic_cast<SwitchButton*>(win))
addSwitchButtonProps(sb, props);
if (auto* pb = dynamic_cast<ProgressBar*>(win))
addProgressBarProps(pb, props);
if (auto* lbl = dynamic_cast<Label*>(win))
addLabelProps(lbl, props);
if (auto* lsb = dynamic_cast<LabeledStaticBox*>(win))
addLabeledStaticBoxProps(lsb, props);
return props;
}
```
- [ ] **Step 3: Write CustomWidgetsPlugin.cpp — addButtonProps**
```cpp
void CustomWidgetsPlugin::addButtonProps(Button* btn,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
wxVector<wxString> styleChoices;
styleChoices.push_back("Regular");
styleChoices.push_back("Confirm");
styleChoices.push_back("Alert");
styleChoices.push_back("Disabled");
auto styleToStr = [](ButtonStyle s) -> wxString {
switch (s) {
case ButtonStyle::Regular: return "Regular";
case ButtonStyle::Confirm: return "Confirm";
case ButtonStyle::Alert: return "Alert";
case ButtonStyle::Disabled: return "Disabled";
}
return "Regular";
};
props.push_back({"Button Style", "Orca Button", PropertyType::Choice,
styleToStr(btn->GetStyle()), false, styleChoices,
[btn, styleToStr]() { return styleToStr(btn->GetStyle()); },
[btn](const wxString& v) {
ButtonStyle s = ButtonStyle::Regular;
if (v == "Confirm") s = ButtonStyle::Confirm;
else if (v == "Alert") s = ButtonStyle::Alert;
else if (v == "Disabled") s = ButtonStyle::Disabled;
btn->SetStyle(s, btn->GetType());
return true;
}});
wxVector<wxString> typeChoices;
typeChoices.push_back("Compact");
typeChoices.push_back("Window");
typeChoices.push_back("Choice");
typeChoices.push_back("Parameter");
typeChoices.push_back("Icon");
typeChoices.push_back("Expanded");
auto typeToStr = [](ButtonType t) -> wxString {
switch (t) {
case ButtonType::Compact: return "Compact";
case ButtonType::Window: return "Window";
case ButtonType::Choice: return "Choice";
case ButtonType::Parameter: return "Parameter";
case ButtonType::Icon: return "Icon";
case ButtonType::Expanded: return "Expanded";
}
return "Compact";
};
props.push_back({"Button Type", "Orca Button", PropertyType::Choice,
typeToStr(btn->GetType()), false, typeChoices,
[btn, typeToStr]() { return typeToStr(btn->GetType()); },
[btn](const wxString& v) {
ButtonType t = ButtonType::Compact;
if (v == "Window") t = ButtonType::Window;
else if (v == "Choice") t = ButtonType::Choice;
else if (v == "Parameter") t = ButtonType::Parameter;
else if (v == "Icon") t = ButtonType::Icon;
else if (v == "Expanded") t = ButtonType::Expanded;
btn->SetStyle(btn->GetStyle(), t);
return true;
}});
props.push_back({"Selected", "Orca Button", PropertyType::Boolean,
btn->IsSelected() ? "true" : "false", false, {},
[btn]() { return btn->IsSelected() ? "true" : "false"; },
[btn](const wxString& v) {
btn->SetSelected(v == "true");
btn->Refresh();
return true;
}});
}
```
- [ ] **Step 4: Write CustomWidgetsPlugin.cpp — addCheckBoxProps**
```cpp
void CustomWidgetsPlugin::addCheckBoxProps(CheckBox* cb,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Half Checked", "Orca CheckBox", PropertyType::Boolean,
cb->IsHalfChecked() ? "true" : "false", false, {},
[cb]() { return cb->IsHalfChecked() ? "true" : "false"; },
[cb](const wxString& v) {
cb->SetHalfChecked(v == "true");
return true;
}});
}
```
- [ ] **Step 5: Write CustomWidgetsPlugin.cpp — addTextInputProps**
```cpp
void CustomWidgetsPlugin::addTextInputProps(TextInput* ti,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Label", "Orca TextInput", PropertyType::String,
ti->GetLabel(), false, {},
[ti]() { return ti->GetLabel(); },
[ti](const wxString& v) { ti->SetLabel(v); return true; }});
props.push_back({"Text Value", "Orca TextInput", PropertyType::String,
ti->GetTextCtrl()->GetValue(), false, {},
[ti]() { return ti->GetTextCtrl()->GetValue(); },
[ti](const wxString& v) { ti->GetTextCtrl()->SetValue(v); return true; }});
props.push_back({"Corner Radius", "Orca TextInput", PropertyType::Integer,
wxString::Format("%d", ti->GetCornerRadius()), false, {},
[ti]() { return wxString::Format("%d", ti->GetCornerRadius()); },
[ti](const wxString& v) {
long val;
if (!v.ToLong(&val)) return false;
ti->SetCornerRadius((double) val);
ti->Refresh();
return true;
}});
}
```
- [ ] **Step 6: Write CustomWidgetsPlugin.cpp — addSwitchButtonProps**
```cpp
void CustomWidgetsPlugin::addSwitchButtonProps(SwitchButton* sb,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Value", "Orca SwitchButton", PropertyType::Boolean,
sb->GetValue() ? "true" : "false", false, {},
[sb]() { return sb->GetValue() ? "true" : "false"; },
[sb](const wxString& v) {
sb->SetValue(v == "true");
return true;
}});
}
```
(Note: `GetValue()` and `SetValue()` are inherited from `wxBitmapToggleButton``wxToggleButton`.)
- [ ] **Step 7: Write CustomWidgetsPlugin.cpp — addProgressBarProps**
```cpp
void CustomWidgetsPlugin::addProgressBarProps(ProgressBar* pb,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Proportion", "Orca ProgressBar", PropertyType::String,
wxString::Format("%.2f", pb->m_proportion), false, {},
[pb]() { return wxString::Format("%.2f", pb->m_proportion); },
[pb](const wxString& v) {
double val;
if (wxSscanf(v, "%lf", &val) != 1) return false;
pb->m_proportion = val;
pb->Refresh();
return true;
}});
props.push_back({"Show Number", "Orca ProgressBar", PropertyType::Boolean,
pb->m_shownumber ? "true" : "false", false, {},
[pb]() { return pb->m_shownumber ? "true" : "false"; },
[pb](const wxString& v) {
pb->m_shownumber = (v == "true");
pb->Refresh();
return true;
}});
}
```
(Note: `m_proportion` and `m_shownumber` are public members on `ProgressBar`.)
- [ ] **Step 8: Write CustomWidgetsPlugin.cpp — addLabelProps**
```cpp
void CustomWidgetsPlugin::addLabelProps(Label* lbl,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
bool isHyperlink = (lbl->GetWindowStyleFlag() & 0x0020) != 0; // LB_HYPERLINK
props.push_back({"Is Hyperlink", "Orca Label", PropertyType::Boolean,
isHyperlink ? "true" : "false", true, {},
[lbl]() {
return (lbl->GetWindowStyleFlag() & 0x0020) ? "true" : "false";
},
nullptr});
props.push_back({"Font Point Size", "Orca Label", PropertyType::ReadOnly,
wxString::Format("%d", lbl->GetFont().GetPointSize()), true, {},
[lbl]() {
return wxString::Format("%d", lbl->GetFont().GetPointSize());
},
nullptr});
}
```
- [ ] **Step 9: Write CustomWidgetsPlugin.cpp — addLabeledStaticBoxProps**
```cpp
void CustomWidgetsPlugin::addLabeledStaticBoxProps(LabeledStaticBox* lsb,
wxVector<wxInspector::PropertyDef>& props)
{
using namespace wxInspector;
props.push_back({"Corner Radius", "LabeledStaticBox", PropertyType::Integer,
wxString::Format("%d", lsb->GetCornerRadius()), false, {},
[lsb]() { return wxString::Format("%d", lsb->GetCornerRadius()); },
[lsb](const wxString& v) {
long val;
if (!v.ToLong(&val)) return false;
lsb->SetCornerRadius((int) val);
return true;
}});
props.push_back({"Border Width", "LabeledStaticBox", PropertyType::Integer,
wxString::Format("%d", lsb->GetBorderWidth()), false, {},
[lsb]() { return wxString::Format("%d", lsb->GetBorderWidth()); },
[lsb](const wxString& v) {
long val;
if (!v.ToLong(&val)) return false;
lsb->SetBorderWidth((int) val);
return true;
}});
// Border Color: display as hex string
wxColour bc = lsb->GetBorderColor().colorForStates(0);
props.push_back({"Border Color", "LabeledStaticBox", PropertyType::String,
bc.GetAsString(wxC2S_HTML_SYNTAX), false, {},
[lsb]() {
return lsb->GetBorderColor()
.colorForStates(0)
.GetAsString(wxC2S_HTML_SYNTAX);
},
[lsb](const wxString& v) {
wxColour c(v);
if (!c.IsOk()) return false;
lsb->SetBorderColor(StateColor(c));
return true;
}});
props.push_back({"Scale", "LabeledStaticBox", PropertyType::ReadOnly,
wxString::Format("%.2f", lsb->GetScale()), true, {},
[lsb]() { return wxString::Format("%.2f", lsb->GetScale()); },
nullptr});
}
```
- [ ] **Step 10: Commit**
```bash
git add src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp
git commit -m "feat: add OrcaCustomWidgets wxInspector plugin
Exposes Orca-specific properties on 7 widget types:
- Button: Style, Type, Selected
- CheckBox: Half Checked
- TextInput: Label, Text Value, Corner Radius
- SwitchButton: Value
- ProgressBar: Proportion, Show Number
- Label: Is Hyperlink, Font Point Size
- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale
Each widget type uses dynamic_cast for safe detection."
```
---
### Task 5: Wire plugins into MainFrame and CMakeLists
**Files:**
- Modify: `src/slic3r/GUI/MainFrame.cpp` (add include + registration call)
- Modify: `src/slic3r/CMakeLists.txt` (add 4 source files)
**Interfaces:**
- Consumes: Tasks 1-4 (all plugins and registration helper)
- Produces: Registered plugins available at runtime, buildable project
- [ ] **Step 1: Add include in MainFrame.cpp**
After the existing includes (around line 30, near the other Utils includes), add:
```cpp
#include "slic3r/Utils/wxInspectorPlugins/Registration.hpp"
```
- [ ] **Step 2: Add registration call in MainFrame constructor**
After `SetupInspectorAccelerator(this);` (currently line ~303), add:
```cpp
RegisterOrcaInspectorPlugins();
```
- [ ] **Step 3: Add source files to CMakeLists.txt**
Find the `SLIC3R_GUI_SOURCES` list in `src/slic3r/CMakeLists.txt`. After the existing `Utils/*.cpp` entries (around line 650-754), add:
```cmake
Utils/wxInspectorPlugins/DPIAwarePlugin.hpp
Utils/wxInspectorPlugins/DPIAwarePlugin.cpp
Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp
Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp
Utils/wxInspectorPlugins/Registration.hpp
```
(Note: Add all 5 files — 2 .hpp + 2 .cpp + 1 Registration.hpp. wxWidgets cmake needs headers listed too for the resource system.)
- [ ] **Step 4: Commit**
```bash
git add src/slic3r/GUI/MainFrame.cpp src/slic3r/CMakeLists.txt
git commit -m "feat: wire wxInspector plugins into MainFrame and build
- Call RegisterOrcaInspectorPlugins() after SetupInspectorAccelerator
- Add all plugin source files to SLIC3R_GUI_SOURCES"
```
---
### Task 6: Build and verify
**Files:**
- None modified (verification only)
- [ ] **Step 1: Configure the build**
```powershell
$cmakePath = "D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe"
& $cmakePath --build . --config Debug --target ALL_BUILD -- -m
```
Expected: Build succeeds with zero errors and zero warnings from our new files.
- [ ] **Step 2: Fix any compilation errors**
If the build fails:
- Check that `#include` paths resolve (the `slic3r/GUI/…` relative paths use `src/` as the include root — verify this is set up in CMake via `include_directories`)
- Check that `ButtonStyle` and `ButtonType` enums are visible (they're defined in `Button.hpp`)
- Check that `StateColor` constructor from `wxColour` is valid (it has `StateColor(wxColour const&)`)
- Check that `LabeledStaticBox::GetBorderColor()` returns by value (StateColor copy is fine)
- On macOS: static box margin removal call needs `#ifdef __WXOSX__` guard
- [ ] **Step 3: Launch OrcaSlicer and verify inspector**
Launch the built OrcaSlicer, press Ctrl+Shift+I to open the inspector:
1. Select the MainFrame in the tree — verify "DPI Scaling" category appears with Scale Factor, Prev Scale Factor, EM Unit, Normal Font, Force Rescale
2. Select an Orca Button — verify "Orca Button" category appears
3. Select an Orca CheckBox — verify "Orca CheckBox" category appears
4. Edit a property value (e.g., Scale Factor) — verify the setter applies correctly
5. Select a LabeledStaticBox — verify corner radius, border width, border color, scale appear
- [ ] **Step 5: Commit (if fixes were needed) or mark complete**
```bash
git status
```
If clean: verification complete. If changes were made: `git add` and commit with fix message.

View File

@@ -0,0 +1,102 @@
# Move `wxInspectable` into `DPIAware` — Design Spec
Date: 2026-07-23
Branch: `dev/layout-inspector`
## Overview
Move the `wxInspector::wxInspectable` base class from individual leaf classes (`DPIDialog`, `MainFrame`) into the common `DPIAware<P>` template. This makes every DPIAware widget automatically visible in the inspector tree without requiring each subclass to opt in.
## Motivation
Currently, only `DPIDialog` and `MainFrame` explicitly inherit `wxInspectable`. `DPIFrame` (which `MainFrame` inherits from) does not — `MainFrame` adds it manually. This means:
- Any `DPIAware<T>` widget that isn't `DPIDialog` or `MainFrame` is invisible in the inspector tree
- `DPIFrame` subclasses (`BaseTransparentDPIFrame`, `ImageDPIFrame`, `ModelMallDialog`, `MediaFileFrame`, `SecondaryCheckDialog`, `PrintErrorDialog`, etc.) don't appear
- Adding a new DPIAware widget type requires remembering to also inherit `wxInspectable`
Moving `wxInspectable` to `DPIAware` fixes this for all current and future DPIAware widgets at once.
## Design
### Change 1: `GUI_Utils.hpp` — `DPIAware<P>`
Add `wxInspector::wxInspectable` as a second base class, and call `SetupInspectorAccelerator(this)` in the constructor (after `this->CenterOnParent()`):
```cpp
// Before:
template<class P> class DPIAware : public P
// After:
template<class P> class DPIAware : public P, public wxInspector::wxInspectable
```
Add in the constructor body (after `this->CenterOnParent()` at line 110):
```cpp
SetupInspectorAccelerator(this);
```
This gives every `DPIAware<T>` widget both inspectability and the Ctrl+Shift+I keyboard shortcut automatically. `#include <wx/inspector/inspector.h>` is already present in the file.
### Change 2: `GUI_Utils.hpp` — `DPIDialog`
Remove the now-redundant `wxInspector::wxInspectable` and the `SetupInspectorAccelerator(this)` call:
```cpp
// Before:
class DPIDialog : public DPIAware<wxDialog>, public wxInspector::wxInspectable
// ...
SetupInspectorAccelerator(this);
// After:
class DPIDialog : public DPIAware<wxDialog>
// (SetupInspectorAccelerator call removed — now done in DPIAware constructor)
```
`DPIDialog` gets `wxInspectable` and the accelerator through `DPIAware<wxDialog>` now.
### Change 3: `MainFrame.hpp` — `MainFrame`
Remove the now-redundant `wxInspector::wxInspectable`:
```cpp
// Before:
class MainFrame : public DPIFrame, public wxInspector::wxInspectable
// After:
class MainFrame : public DPIFrame
```
`MainFrame` gets `wxInspectable` through `DPIFrame``DPIAware<wxFrame>`.
### Change 4: `MainFrame.cpp` — `MainFrame` constructor
Remove the now-redundant `SetupInspectorAccelerator(this)` call (line 304). It will be called automatically by the `DPIAware` constructor.
## Impact
| Widget | Before | After |
|--------|--------|-------|
| `DPIDialog` subclasses (~80) | ✓ inspectable | ✓ inspectable (transitive) |
| `MainFrame` | ✓ inspectable | ✓ inspectable (transitive) |
| `DPIFrame` subclasses (8 others) | ✗ invisible | ✓ inspectable |
| Future `DPIAware<T>` | ✗ invisible | ✓ inspectable |
## Files Modified
| File | Change |
|------|--------|
| `src/slic3r/GUI/GUI_Utils.hpp` | `DPIAware<P>` gains `wxInspector::wxInspectable` + `SetupInspectorAccelerator(this)` call; `DPIDialog` drops redundant `wxInspector::wxInspectable` and `SetupInspectorAccelerator(this)` |
| `src/slic3r/GUI/MainFrame.hpp` | `MainFrame` drops redundant `wxInspector::wxInspectable` |
| `src/slic3r/GUI/MainFrame.cpp` | Remove redundant `SetupInspectorAccelerator(this)` from MainFrame constructor |
## Non-Goals
- The `DPIAwarePlugin` detection logic (`dynamic_cast<DPIFrame*>` / `dynamic_cast<DPIDialog*>`) is unchanged
- No new DPI properties — this is purely about tree visibility and accelerator setup
## Risk Assessment
- **Multiple inheritance**: `DPIAware<P>` already has a vtable (virtual destructor). Adding `wxInspectable` adds a second base but no additional data members. The `wxInspector::wxInspectable` class is expected to be a lightweight marker interface.
- **Build**: No new includes needed; `<wx/inspector/inspector.h>` is already included in `GUI_Utils.hpp`.
- **Cross-platform**: The change is standard C++ multiple inheritance — no platform-specific concerns.

View File

@@ -0,0 +1,244 @@
# wxInspector Plugins for OrcaSlicer Custom Controls — Design Spec
Date: 2026-07-23
Branch: `dev/layout-inspector`
## Overview
Create wxInspector plugins that expose OrcaSlicer's custom widget properties in the inspector's property grid. Without these plugins, the inspector shows only generic wxWidgets properties — missing all DPI-awareness data, custom styling, and Orca-specific control state.
## Goals
1. **DPIAware properties** — Inspect and update `scale_factor`, `prev_scale_factor`, `em_unit`, and `normal_font` on any DPIAware-derived widget
2. **Custom widget properties** — Surface Orca-specific properties on `Button`, `CheckBox`, `TextInput`, `SwitchButton`, `ProgressBar`, `Label`, and `LabeledStaticBox`
3. **Minimal source changes** — Only add trivial (one-line) getters/setters to existing classes; no architectural refactoring of Orca's widget hierarchy
## Non-Goals
- Custom inspector panels or AUI tabs (use the existing property grid and method invoker)
- Python-plugin integration (this is C++ wxInspector, not Orca's Python plugin system)
- Event logging customization (the built-in event logger already works)
## Architecture
### Two Plugins
| Plugin | Class | Files |
|--------|-------|-------|
| DPIAware plugin | `DPIAwarePlugin` | `DPIAwarePlugin.hpp`, `DPIAwarePlugin.cpp` |
| Custom widgets plugin | `CustomWidgetsPlugin` | `CustomWidgetsPlugin.hpp`, `CustomWidgetsPlugin.cpp` |
| Registration helper | inline function | `Registration.hpp` |
All files live under `src/slic3r/Utils/wxInspectorPlugins/`.
### Plugin Detection Strategy
**DPIAware plugin**: Uses `dynamic_cast<DPIFrame*>` and `dynamic_cast<DPIDialog*>` as detection gates. `DPIFrame` = `DPIAware<wxFrame>`, `DPIDialog` = `DPIAware<wxDialog>`. Since these are concrete typedefs, `dynamic_cast` works at runtime. This covers `MainFrame`, `SettingsDialog`, and all 8 calibration dialogs (which inherit `DPIDialog`).
**Custom widgets plugin**: Gates broadly on `CLASSINFO(wxWindow)`, then uses per-type `dynamic_cast` inside `GetProperties` to check each Orca-specific type. Only matching types append properties.
### Registration
A single `RegisterOrcaInspectorPlugins()` inline function in `Registration.hpp` creates both plugins as function-local statics (matching the wxInspector built-in provider pattern) and registers them via `wxInspector::RegisterPlugin()`.
Called once from `MainFrame::MainFrame()` after `SetupInspectorAccelerator(this)`.
### Why Separate Plugins?
- DPIAware is a C++ template concept (not a wxClassInfo-isKindOf check), so it needs its own detection logic
- Custom widgets use standard wxClassInfo-based detection, matching the built-in provider pattern
- Two focused files are easier to review and maintain than one monolithic plugin
- Compile-time failure isolation: if a widget header changes, only one plugin breaks
## DPIAware Plugin — Property Specification
### Source Changes (GUI_Utils.hpp)
Four one-liner methods added to the `DPIAware<P>` template class (public section):
```cpp
float scale_factor() const { return m_scale_factor; } // already exists
float prev_scale_factor() const { return m_prev_scale_factor; } // already exists
int em_unit() const { return m_em_unit; } // already exists
void set_scale_factor(float v) { m_scale_factor = v; } // NEW
void set_prev_scale_factor(float v) { m_prev_scale_factor = v; } // NEW
void set_em_unit(int v) { m_em_unit = v; } // NEW
bool force_rescale() const { return m_force_rescale; } // NEW
// m_normal_font getter already exists: normal_font()
```
### Detection
```cpp
bool CanProvideProperties(wxClassInfo* info) override {
// Gated in GetProperties via dynamic_cast on the window itself
return info->IsKindOf(CLASSINFO(wxWindow));
}
```
In `GetProperties`:
```cpp
auto* win = obj.AsWindow();
bool isDPI = dynamic_cast<DPIFrame*>(win) || dynamic_cast<DPIDialog*>(win);
if (!isDPI) return props;
```
### Property Table (category: "DPI Scaling")
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Scale Factor | String (float) | Yes | `dpi->scale_factor()` | `dpi->set_scale_factor(v)` |
| Prev Scale Factor | String (float) | Yes | `dpi->prev_scale_factor()` | `dpi->set_prev_scale_factor(v)` |
| EM Unit | Integer | Yes | `dpi->em_unit()` | `dpi->set_em_unit(v)` |
| Normal Font | ReadOnly | No | `dpi->normal_font().GetNativeFontInfoDesc()` | — |
| Force Rescale | Boolean (ReadOnly) | No | `dpi->force_rescale()` | — |
**Note on setters**: The setters simply store values. They do NOT trigger a widget rescale/layout. To see the effect of a changed scale factor, use the inspector's Methods panel to call `Layout()` or resize the window — which triggers the DPI_CHANGED event path naturally.
## Custom Widgets Plugin — Property Specification
All properties are appended to the built-in wxWindow properties. Each widget type is independently detected via `dynamic_cast`.
### Detection gates (in `GetProperties`)
```cpp
auto* win = obj.AsWindow();
if (auto* btn = dynamic_cast<Button*>(win)) { addButtonProperties(btn, props); }
if (auto* cb = dynamic_cast<CheckBox*>(win)) { addCheckBoxProperties(cb, props); }
if (auto* ti = dynamic_cast<TextInput*>(win)) { addTextInputProperties(ti, props); }
if (auto* sb = dynamic_cast<SwitchButton*>(win)) { addSwitchButtonProperties(sb, props); }
if (auto* pb = dynamic_cast<ProgressBar*>(win)) { addProgressBarProperties(pb, props); }
if (auto* lbl = dynamic_cast<Label*>(win)) { addLabelProperties(lbl, props); }
if (auto* lsb = dynamic_cast<LabeledStaticBox*>(win)) { addLabeledStaticBoxProperties(lsb, props); }
```
### Orca Button (`Button`) — category: "Orca Button"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Button Style | Choice | Yes | enum→string | string→enum |
| Button Type | Choice | Yes | enum→string | string→enum |
| Selected | Boolean | Yes | `m_selected` (needs getter) | `SetSelected(v)` |
| Active Icon | ReadOnly | No | icon name string | — |
| Inactive Icon | ReadOnly | No | icon name string | — |
Choices for Button Style: `Regular`, `Confirm`, `Alert`, `Disabled`
Choices for Button Type: `Compact`, `Window`, `Choice`, `Parameter`, `Icon`, `Expanded`
**Source changes needed**: Button's `m_selected` is private. Add one-liner getter:
```cpp
bool IsSelected() const { return m_selected; }
```
### Orca CheckBox (`CheckBox`) — category: "Orca CheckBox"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Half Checked | Boolean | Yes | `m_half_checked` (needs getter) | `SetHalfChecked(v)` |
**Source changes needed**: `m_half_checked` is private. Add one-liner getter:
```cpp
bool IsHalfChecked() const { return m_half_checked; }
```
### Orca TextInput (`TextInput`) — category: "Orca TextInput"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Label | String | Yes | `GetLabel()` (inherited from wxWindow) | `SetLabel(v)` (exists) |
| Text Value | String | Yes | `GetTextCtrl()->GetValue()` (GetTextCtrl is public) | `GetTextCtrl()->SetValue(v)` |
| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) |
**Source changes needed**: Add one getter to `TextInput`:
```cpp
int GetCornerRadius() const { return static_cast<int>(radius); }
```
(`radius` is inherited from StaticBox. `SetCornerRadius(double)` already exists. `GetTextCtrl()` is already public.)
### Orca SwitchButton (`SwitchButton`) — category: "Orca SwitchButton"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Value | Boolean | Yes | existing getter | existing setter |
### Orca ProgressBar (`ProgressBar`) — category: "Orca ProgressBar"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Proportion | Float (0-1) | Yes | `pb->m_proportion` (public member) | `pb->m_proportion = v` |
| Show Number | Boolean | Yes | `pb->m_shownumber` (public member) | `pb->m_shownumber = v` |
**No source changes needed**: `m_proportion` and `m_shownumber` are already public members. `SetValue(int)` and `SetProgress(int)` already exist as public methods.
### Orca Label (`Label`) — category: "Orca Label"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Is Hyperlink | Boolean | No | existing flag check | — |
| Font Size | ReadOnly | No | `GetFont().GetPointSize()` | — |
### LabeledStaticBox — category: "LabeledStaticBox"
| Name | Type | Editable | Getter | Setter |
|------|------|----------|--------|--------|
| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) |
| Border Width | Integer | Yes | `GetBorderWidth()` (NEW) | `SetBorderWidth(v)` (exists) |
| Border Color | String (hex) | Yes | `GetBorderColor()` (NEW) | `SetBorderColor(v)` (exists) |
| Scale | Float (ReadOnly) | No | `m_scale` (protected, needs getter) | — |
**Source changes needed**: Four one-liner getters added to `LabeledStaticBox`:
```cpp
int GetCornerRadius() const { return m_radius; }
int GetBorderWidth() const { return m_border_width; }
StateColor GetBorderColor() const { return border_color; }
float GetScale() const { return m_scale; }
```
## Files Modified (Existing Code)
| File | Changes |
|------|---------|
| `src/slic3r/GUI/GUI_Utils.hpp` | +4 methods in `DPIAware<P>`: `set_scale_factor()`, `set_prev_scale_factor()`, `set_em_unit()`, `force_rescale()` |
| `src/slic3r/GUI/Widgets/LabeledStaticBox.hpp` | +4 getter declarations: `GetCornerRadius()`, `GetBorderWidth()`, `GetBorderColor()`, `GetScale()` |
| `src/slic3r/GUI/Widgets/LabeledStaticBox.cpp` | +4 getter implementations |
| `src/slic3r/GUI/Widgets/Button.hpp` | +1 getter: `IsSelected()` |
| `src/slic3r/GUI/Widgets/CheckBox.hpp` | +1 getter: `IsHalfChecked()` |
| `src/slic3r/GUI/Widgets/TextInput.hpp` | +1 getter: `GetCornerRadius()` |
| `src/slic3r/GUI/Widgets/ProgressBar.hpp` | None (public members are used directly) |
| `src/slic3r/GUI/MainFrame.cpp` | +1 `#include`, +1 call to `RegisterOrcaInspectorPlugins()` |
| `src/slic3r/CMakeLists.txt` | +4 entries in `SLIC3R_GUI_SOURCES` (the .cpp plugin files) |
## Files Created
```
src/slic3r/Utils/wxInspectorPlugins/
├── DPIAwarePlugin.hpp
├── DPIAwarePlugin.cpp
├── CustomWidgetsPlugin.hpp
├── CustomWidgetsPlugin.cpp
└── Registration.hpp
```
## Build & Linking
The `wxInspector` dependency is already wired:
- `deps/wxInspector/wxInspector.cmake` fetches and builds wxInspector
- `src/CMakeLists.txt` lines 92-93 link `wxInspector::wxInspector` into `wxWidgets_LIBRARIES`
- The plugin files only need `#include <wx/inspector/plugin.h>` and `#include <wx/inspector/inspector.h>` — both available from the installed dependency
No new CMake dependencies needed. Only the new source files need listing in `SLIC3R_GUI_SOURCES`.
## Error Handling & Edge Cases
- **Stale pointers**: Plugin lambdas capture raw pointers, regenerated on every `GetProperties` call (matching wxInspector's built-in provider pattern). Pointers live only until the next tree selection.
- **Widget destruction**: If a widget is destroyed while the inspector is showing its properties, `InspectableObject::IsValid()` returns false and properties are not displayed. The inspector won't show stale data.
- **Invalid property values**: Setters use `sscanf` / `ToLong` with validation (matching built-in patterns). Bogus input is rejected — setter returns `false`, property grid shows error state.
- **DPI drift**: Setting `scale_factor` without triggering rescale means displayed sizes don't match the new factor. This is acceptable — the inspector is a developer tool; operators know to call `Layout()` after making changes.
- **Missing widget type**: If a `dynamic_cast` fails for all types, only built-in wxWindow properties are shown. No crash, no error — just reduced info.
## Future Work (Out of Scope)
- **StateColor visualization**: `StateColor` is a multi-value type (maps bitmask states to colors). A full solution would need a custom property editor (e.g., a table showing each state→color pair). Keep it simple for now.
- **ScalableBitmap display**: Could show the bitmap as an inline thumbnail. Complex property editor work — deferred.
- **More widget types**: `SwitchBoard`, `MultiSwitchButton`, `StepCtrl`, `FanControl`, `DropDown`, `ComboBox`, `AMS*` widgets could all benefit. Add as needed.
- **Property refresh on tree selection**: Currently properties are static snapshots. A "refresh" button or auto-poll could keep values current for rapidly-changing widgets (progress bars, etc.). The built-in wxInspector already provides a tree-refresh button.

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-20 10:39-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -552,6 +552,7 @@ msgstr ""
msgid "Reset"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Enter"
msgstr ""
@@ -877,6 +878,7 @@ msgstr ""
msgid "Select all connectors"
msgstr ""
msgctxt "Cut tool"
msgid "Cut"
msgstr ""
@@ -980,7 +982,7 @@ msgstr ""
#, possible-boost-format
msgid ""
"Objects(%1%) have duplicated connectors. Some connectors may be missing in slicing result.\n"
"Please report to PrusaSlicer team in which scenario this issue happened.\n"
"Please report to the OrcaSlicer team in which scenario this issue happened.\n"
"Thank you."
msgstr ""
@@ -1732,12 +1734,14 @@ msgstr ""
msgid "Select point"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Delete"
msgstr ""
msgid "Restart selection"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Esc"
msgstr ""
@@ -1838,6 +1842,7 @@ msgstr ""
msgid "Leaving Measure gizmo"
msgstr ""
msgctxt "Assembly tool"
msgid "Assemble"
msgstr ""
@@ -2348,6 +2353,9 @@ msgstr ""
msgid "Import File"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Choose files"
msgstr ""
@@ -2466,12 +2474,14 @@ msgstr ""
msgid "Show"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Del"
msgstr ""
msgid "Delete the selected object"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Backspace"
msgstr ""
@@ -2659,6 +2669,9 @@ msgstr ""
msgid "Restore to Meter"
msgstr ""
msgid "Assemble"
msgstr ""
msgid "Assemble the selected objects into an object with multiple parts"
msgstr ""
@@ -3372,7 +3385,6 @@ msgstr ""
msgid "Innerloop"
msgstr ""
#. TRN To be shown in the main menu View->Top
msgid "Top"
msgstr ""
@@ -3403,6 +3415,7 @@ msgctxt "air_duct"
msgid "Left(Aux)"
msgstr ""
msgctxt "Hotend Heat Breaker Fan"
msgid "Hotend"
msgstr ""
@@ -3595,6 +3608,9 @@ msgstr ""
msgid "Arranging"
msgstr ""
msgid "Arranging "
msgstr ""
msgid "Arranging canceled."
msgstr ""
@@ -4120,6 +4136,14 @@ msgstr ""
msgid "Print with filament in AMS"
msgstr ""
msgctxt "Nozzle position"
msgid "Left"
msgstr ""
msgctxt "Nozzle position"
msgid "Right"
msgstr ""
msgid "When the current material run out, the printer will continue to print in the following order."
msgstr ""
@@ -5347,6 +5371,7 @@ msgstr ""
msgid "Quality / Speed"
msgstr ""
msgctxt "Mesh action"
msgid "Smooth"
msgstr ""
@@ -5447,10 +5472,24 @@ msgstr ""
msgid "Align to Y axis"
msgstr ""
msgctxt "Camera View"
msgid "Front"
msgstr ""
msgctxt "Camera View"
msgid "Back"
msgstr ""
#. TRN To be shown in the main menu View->Top
msgctxt "Camera View"
msgid "Top"
msgstr ""
#. TRN To be shown in the main menu View->Bottom
msgctxt "Camera View"
msgid "Bottom"
msgstr ""
msgctxt "Camera View"
msgid "Left"
msgstr ""
@@ -5831,19 +5870,13 @@ msgstr ""
msgid "Top View"
msgstr ""
#. TRN To be shown in the main menu View->Bottom
msgid "Bottom"
msgstr ""
msgid "Bottom View"
msgstr ""
msgid "Front"
msgstr ""
msgid "Front View"
msgstr ""
msgctxt "Camera View"
msgid "Rear"
msgstr ""
@@ -5955,6 +5988,9 @@ msgstr ""
msgid "Redo"
msgstr ""
msgid "Cut"
msgstr ""
msgid "Cut selection to clipboard"
msgstr ""
@@ -6054,6 +6090,10 @@ msgstr ""
msgid "Preferences"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr ""
msgid "View"
msgstr ""
@@ -8615,6 +8655,26 @@ msgstr ""
msgid "Displays current viewport FPS in the top-right corner."
msgstr ""
msgid "G-code Preview"
msgstr ""
msgid "Dim lower layers"
msgstr ""
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr ""
msgid "Dimmed layer brightness"
msgstr ""
msgid "%"
msgstr ""
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
msgid "Login region"
msgstr ""
@@ -8897,6 +8957,7 @@ msgstr ""
msgid "The selected preset is null!"
msgstr ""
msgctxt "Layer range"
msgid "End"
msgstr ""
@@ -9180,6 +9241,9 @@ msgstr ""
msgid "The current printer does not support timelapse in Traditional Mode when printing By-Object."
msgstr ""
msgid "I have checked the installed nozzle and want to print anyway."
msgstr ""
msgid "Errors"
msgstr ""
@@ -10804,24 +10868,28 @@ msgstr ""
msgid "Select objects by rectangle"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Up"
msgstr ""
msgid "Move selection 10mm in positive Y direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Down"
msgstr ""
msgid "Move selection 10mm in negative Y direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Left"
msgstr ""
msgid "Move selection 10mm in negative X direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Right"
msgstr ""
@@ -10906,9 +10974,17 @@ msgstr ""
msgid "Toggle printable for object/part"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Tab"
msgstr ""
msgid "Switch between Prepare/Preview"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Space"
msgstr ""
msgid "Open actions speed dial"
msgstr ""
@@ -10966,9 +11042,17 @@ msgstr ""
msgid "Move slider 5x faster"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Home"
msgstr ""
msgid "Horizontal slider - Move to start position"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "End"
msgstr ""
msgid "Horizontal slider - Move to last position"
msgstr ""
@@ -12287,12 +12371,26 @@ msgstr ""
msgid "Intra-layer order"
msgstr ""
msgid "Print order within a single layer."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
msgid "As object list"
msgstr ""
msgid "Best of all (shortest path)"
msgstr ""
msgid "Snake"
msgstr ""
msgid "Slow printing down for better layer cooling"
msgstr ""
@@ -14666,7 +14764,7 @@ msgstr ""
msgid "Retract amount after wipe"
msgstr ""
#, possible-c-format
#, no-c-format, no-boost-format
msgid ""
"The length of fast retraction after wipe, relative to retraction length.\n"
"The value will be clamped by 100% minus the retract amount before the wipe value."
@@ -15098,6 +15196,9 @@ msgstr ""
msgid "Traditional"
msgstr ""
msgid "Smooth"
msgstr ""
msgid "Farthest point timelapse"
msgstr ""
@@ -16001,6 +16102,12 @@ msgstr ""
msgid "Whether this printer supports fast purge mode with optimized temperature and multiplier."
msgstr ""
msgid "Filament change"
msgstr ""
msgid "The volume of material required to prime the extruder on the tower, excluding a hotend change."
msgstr ""
msgid "The volume of material required to prime the extruder for a hotend change on the tower."
msgstr ""
@@ -17139,6 +17246,15 @@ msgid ""
"Please select one that should be used."
msgstr ""
msgid "Auto-scale for nozzle"
msgstr ""
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
msgid "PA Calibration"
msgstr ""
@@ -17261,6 +17377,12 @@ msgstr ""
msgid "End speed: "
msgstr ""
msgid "Auto-adjust to max volumetric speed"
msgstr ""
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr ""
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -17268,6 +17390,39 @@ msgid ""
"end > start + step"
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
msgid "Continue anyway?"
msgstr ""
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Start retraction length: "
msgstr ""
@@ -19049,15 +19204,15 @@ msgstr ""
msgid "Show details"
msgstr ""
msgid "Hide details"
msgstr ""
msgid "Version to install:"
msgstr ""
msgid "Download and Install"
msgstr ""
msgid "Skip for Now"
msgstr ""
msgid "A new version of the Bambu Network Plug-in is available."
msgstr ""
@@ -19068,18 +19223,12 @@ msgstr ""
msgid "Update to version:"
msgstr ""
msgid "Update Now"
msgstr ""
msgid "Remind Later"
msgstr ""
msgid "Skip Version"
msgstr ""
msgid "Don't Ask Again"
msgstr ""
msgid "Update Now"
msgstr ""
msgid "(Latest)"
msgstr ""
@@ -19095,9 +19244,6 @@ msgstr ""
msgid "Restart Now"
msgstr ""
msgid "Restart Later"
msgstr ""
msgid "NO RAMMING AT ALL"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-20 10:39-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-06-17 15:44-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: \n"
@@ -548,6 +548,7 @@ msgstr ""
msgid "Reset"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Enter"
msgstr ""
@@ -873,6 +874,7 @@ msgstr ""
msgid "Select all connectors"
msgstr ""
msgctxt "Cut tool"
msgid "Cut"
msgstr ""
@@ -976,7 +978,7 @@ msgstr ""
#, boost-format
msgid ""
"Objects(%1%) have duplicated connectors. Some connectors may be missing in slicing result.\n"
"Please report to PrusaSlicer team in which scenario this issue happened.\n"
"Please report to the OrcaSlicer team in which scenario this issue happened.\n"
"Thank you."
msgstr ""
@@ -1728,12 +1730,14 @@ msgstr ""
msgid "Select point"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Delete"
msgstr ""
msgid "Restart selection"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Esc"
msgstr ""
@@ -1834,6 +1838,7 @@ msgstr ""
msgid "Leaving Measure gizmo"
msgstr ""
msgctxt "Assembly tool"
msgid "Assemble"
msgstr ""
@@ -2344,6 +2349,9 @@ msgstr ""
msgid "Import File"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Choose files"
msgstr ""
@@ -2462,12 +2470,14 @@ msgstr ""
msgid "Show"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Del"
msgstr ""
msgid "Delete the selected object"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Backspace"
msgstr ""
@@ -2655,6 +2665,9 @@ msgstr ""
msgid "Restore to Meter"
msgstr ""
msgid "Assemble"
msgstr ""
msgid "Assemble the selected objects into an object with multiple parts"
msgstr ""
@@ -3368,7 +3381,6 @@ msgstr ""
msgid "Innerloop"
msgstr ""
#. TRN To be shown in the main menu View->Top
msgid "Top"
msgstr ""
@@ -3399,6 +3411,7 @@ msgctxt "air_duct"
msgid "Left(Aux)"
msgstr ""
msgctxt "Hotend Heat Breaker Fan"
msgid "Hotend"
msgstr ""
@@ -3591,6 +3604,9 @@ msgstr ""
msgid "Arranging"
msgstr ""
msgid "Arranging "
msgstr ""
msgid "Arranging canceled."
msgstr ""
@@ -4116,6 +4132,14 @@ msgstr ""
msgid "Print with filament in AMS"
msgstr ""
msgctxt "Nozzle position"
msgid "Left"
msgstr ""
msgctxt "Nozzle position"
msgid "Right"
msgstr ""
msgid "When the current material run out, the printer will continue to print in the following order."
msgstr ""
@@ -5343,6 +5367,7 @@ msgstr ""
msgid "Quality / Speed"
msgstr ""
msgctxt "Mesh action"
msgid "Smooth"
msgstr ""
@@ -5443,10 +5468,24 @@ msgstr ""
msgid "Align to Y axis"
msgstr ""
msgctxt "Camera View"
msgid "Front"
msgstr ""
msgctxt "Camera View"
msgid "Back"
msgstr ""
#. TRN To be shown in the main menu View->Top
msgctxt "Camera View"
msgid "Top"
msgstr ""
#. TRN To be shown in the main menu View->Bottom
msgctxt "Camera View"
msgid "Bottom"
msgstr ""
msgctxt "Camera View"
msgid "Left"
msgstr ""
@@ -5827,19 +5866,13 @@ msgstr ""
msgid "Top View"
msgstr ""
#. TRN To be shown in the main menu View->Bottom
msgid "Bottom"
msgstr ""
msgid "Bottom View"
msgstr ""
msgid "Front"
msgstr ""
msgid "Front View"
msgstr ""
msgctxt "Camera View"
msgid "Rear"
msgstr ""
@@ -5951,6 +5984,9 @@ msgstr ""
msgid "Redo"
msgstr ""
msgid "Cut"
msgstr ""
msgid "Cut selection to clipboard"
msgstr ""
@@ -6050,6 +6086,10 @@ msgstr ""
msgid "Preferences"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr ""
msgid "View"
msgstr ""
@@ -8611,6 +8651,26 @@ msgstr ""
msgid "Displays current viewport FPS in the top-right corner."
msgstr ""
msgid "G-code Preview"
msgstr ""
msgid "Dim lower layers"
msgstr ""
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr ""
msgid "Dimmed layer brightness"
msgstr ""
msgid "%"
msgstr ""
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
msgid "Login region"
msgstr ""
@@ -8893,6 +8953,7 @@ msgstr ""
msgid "The selected preset is null!"
msgstr ""
msgctxt "Layer range"
msgid "End"
msgstr ""
@@ -9176,6 +9237,9 @@ msgstr ""
msgid "The current printer does not support timelapse in Traditional Mode when printing By-Object."
msgstr ""
msgid "I have checked the installed nozzle and want to print anyway."
msgstr ""
msgid "Errors"
msgstr ""
@@ -10800,24 +10864,28 @@ msgstr ""
msgid "Select objects by rectangle"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Up"
msgstr ""
msgid "Move selection 10mm in positive Y direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Down"
msgstr ""
msgid "Move selection 10mm in negative Y direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Left"
msgstr ""
msgid "Move selection 10mm in negative X direction"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Arrow Right"
msgstr ""
@@ -10902,9 +10970,17 @@ msgstr ""
msgid "Toggle printable for object/part"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Tab"
msgstr ""
msgid "Switch between Prepare/Preview"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Space"
msgstr ""
msgid "Open actions speed dial"
msgstr ""
@@ -10962,9 +11038,17 @@ msgstr ""
msgid "Move slider 5x faster"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "Home"
msgstr ""
msgid "Horizontal slider - Move to start position"
msgstr ""
msgctxt "Keyboard Shortcut"
msgid "End"
msgstr ""
msgid "Horizontal slider - Move to last position"
msgstr ""
@@ -12283,12 +12367,26 @@ msgstr ""
msgid "Intra-layer order"
msgstr ""
msgid "Print order within a single layer."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
msgid "As object list"
msgstr ""
msgid "Best of all (shortest path)"
msgstr ""
msgid "Snake"
msgstr ""
msgid "Slow printing down for better layer cooling"
msgstr ""
@@ -14662,7 +14760,7 @@ msgstr ""
msgid "Retract amount after wipe"
msgstr ""
#, c-format
#, no-c-format, no-boost-format
msgid ""
"The length of fast retraction after wipe, relative to retraction length.\n"
"The value will be clamped by 100% minus the retract amount before the wipe value."
@@ -15094,6 +15192,9 @@ msgstr ""
msgid "Traditional"
msgstr ""
msgid "Smooth"
msgstr ""
msgid "Farthest point timelapse"
msgstr ""
@@ -15997,6 +16098,12 @@ msgstr ""
msgid "Whether this printer supports fast purge mode with optimized temperature and multiplier."
msgstr ""
msgid "Filament change"
msgstr ""
msgid "The volume of material required to prime the extruder on the tower, excluding a hotend change."
msgstr ""
msgid "The volume of material required to prime the extruder for a hotend change on the tower."
msgstr ""
@@ -17135,6 +17242,15 @@ msgid ""
"Please select one that should be used."
msgstr ""
msgid "Auto-scale for nozzle"
msgstr ""
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
msgid "PA Calibration"
msgstr ""
@@ -17257,6 +17373,12 @@ msgstr ""
msgid "End speed: "
msgstr ""
msgid "Auto-adjust to max volumetric speed"
msgstr ""
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr ""
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -17264,6 +17386,39 @@ msgid ""
"end > start + step"
msgstr ""
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
msgid "Continue anyway?"
msgstr ""
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Start retraction length: "
msgstr ""
@@ -19045,15 +19200,15 @@ msgstr ""
msgid "Show details"
msgstr ""
msgid "Hide details"
msgstr ""
msgid "Version to install:"
msgstr ""
msgid "Download and Install"
msgstr ""
msgid "Skip for Now"
msgstr ""
msgid "A new version of the Bambu Network Plug-in is available."
msgstr ""
@@ -19064,18 +19219,12 @@ msgstr ""
msgid "Update to version:"
msgstr ""
msgid "Update Now"
msgstr ""
msgid "Remind Later"
msgstr ""
msgid "Skip Version"
msgstr ""
msgid "Don't Ask Again"
msgstr ""
msgid "Update Now"
msgstr ""
msgid "(Latest)"
msgstr ""
@@ -19091,9 +19240,6 @@ msgstr ""
msgid "Restart Now"
msgstr ""
msgid "Restart Later"
msgstr ""
msgid "NO RAMMING AT ALL"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_add.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="16"
inkscape:cx="1.625"
inkscape:cy="10.84375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" />
<path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" />
<path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" />
<path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" />
<path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" />
<path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" />
<path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" />
<rect
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect1"
width="22.449276"
height="1.000026"
x="8.7753611"
y="19.499987"
ry="0.50001299" />
<path
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect2"
width="22.449276"
height="1.000026"
x="8.775362"
y="-20.500011"
transform="rotate(90)"
sodipodi:type="rect"
inkscape:path-effect="#path-effect3"
d="M 9.2741959,-20.500011 H 30.737917 a 0.48672135,0.48672135 45 0 1 0.486721,0.486721 0.51978097,0.51978097 135.70952 0 1 -0.526178,0.513305 H 9.2753766 A 0.50001454,0.50001454 45 0 1 8.775362,-20 v -0.0012 a 0.49883385,0.49883385 135 0 1 0.4988339,-0.498833 z" />
</svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_add_negative.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="45.254834"
inkscape:cx="16.46233"
inkscape:cy="19.301805"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" />
<path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" />
<path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" />
<path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" />
<path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" />
<path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" />
<path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" />
<rect
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect1"
width="22.449276"
height="1.000026"
x="8.7753611"
y="19.499987"
ry="0.50001299" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1,330 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_group.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="25.28125"
inkscape:cy="18.359375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><g
id="g31"
transform="translate(-1.2926181,-1.328125)"><path
d="m 11.472656,12.726388 v 6.047224 A 1.2263883,1.2263883 45 0 0 12.699044,20 h 6.074568 A 1.2263883,1.2263883 135 0 0 20,18.773612 V 12.726388 A 1.2263883,1.2263883 45 0 0 18.773612,11.5 h -6.074568 a 1.2263883,1.2263883 135 0 0 -1.226388,1.226388 z m 1.730947,-0.0096 h 5.065451 a 0.47508735,0.47508735 45 0 1 0.475087,0.475087 v 5.303732 a 0.47508735,0.47508735 135 0 1 -0.475087,0.475087 h -5.065451 a 0.47508735,0.47508735 45 0 1 -0.475087,-0.475087 l 0,-5.303732 a 0.47508735,0.47508735 135 0 1 0.475087,-0.475087 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path30"
inkscape:path-effect="#path-effect31"
inkscape:original-d="M 11.472656,11.5 V 20 H 20 v -8.5 z m 1.25586,1.216797 h 6.015625 v 6.253906 h -6.015625 z" /><path
d="m 11.472656,12.726388 v 6.047224 A 1.2263883,1.2263883 45 0 0 12.699044,20 h 6.074568 A 1.2263883,1.2263883 135 0 0 20,18.773612 V 12.726388 A 1.2263883,1.2263883 45 0 0 18.773612,11.5 h -6.074568 a 1.2263883,1.2263883 135 0 0 -1.226388,1.226388 z m 1.730947,-0.0096 h 5.065451 a 0.47508735,0.47508735 45 0 1 0.475087,0.475087 v 5.303732 a 0.47508735,0.47508735 135 0 1 -0.475087,0.475087 h -5.065451 a 0.47508735,0.47508735 45 0 1 -0.475087,-0.475087 l 0,-5.303732 a 0.47508735,0.47508735 135 0 1 0.475087,-0.475087 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path30-4"
inkscape:path-effect="#path-effect31-9"
inkscape:original-d="M 11.472656,11.5 V 20 H 20 v -8.5 z m 1.25586,1.216797 h 6.015625 v 6.253906 h -6.015625 z"
transform="translate(11.058594)" /><path
d="m 11.472656,12.726388 v 6.047224 A 1.2263883,1.2263883 45 0 0 12.699044,20 h 6.074568 A 1.2263883,1.2263883 135 0 0 20,18.773612 V 12.726388 A 1.2263883,1.2263883 45 0 0 18.773612,11.5 h -6.074568 a 1.2263883,1.2263883 135 0 0 -1.226388,1.226388 z m 1.730947,-0.0096 h 5.065451 a 0.47508735,0.47508735 45 0 1 0.475087,0.475087 v 5.303732 a 0.47508735,0.47508735 135 0 1 -0.475087,0.475087 h -5.065451 a 0.47508735,0.47508735 45 0 1 -0.475087,-0.475087 l 0,-5.303732 a 0.47508735,0.47508735 135 0 1 0.475087,-0.475087 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path30-4-3"
inkscape:path-effect="#path-effect31-9-1"
inkscape:original-d="M 11.472656,11.5 V 20 H 20 v -8.5 z m 1.25586,1.216797 h 6.015625 v 6.253906 h -6.015625 z"
transform="translate(0,11.15625)" /></g></svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_checker.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect251"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.896875,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect229"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,0.49844035,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49844035,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect206"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0051443,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0058593,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0051442,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.003906,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="22.627417"
inkscape:cx="45.519999"
inkscape:cy="5.4579805"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path251"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="m 7.8085938,8.3265625 v 0.103125 10.8320315 1 10.832031 a 1,1 45 0 0 1,1 h 10.8320312 1 10.832031 a 1,1 135 0 0 1,-1 v -10.832031 -1 -10.8320315 a 1,1 45 0 0 -1,-1 h -10.832031 -1 -10.8320312 -0.103125 a 0.896875,0.896875 135 0 0 -0.896875,0.896875 z M 20.640625,8.4296875 H 31.472656 V 19.261719 H 20.640625 Z M 8.8085938,20.261719 H 19.640625 V 31.09375 H 8.8085938 Z"
inkscape:path-effect="#path-effect251"
inkscape:original-d="m 7.8085938,7.4296875 v 1 10.8320315 1 10.832031 1 h 1 10.8320312 1 10.832031 1 v -1 -10.832031 -1 -10.8320315 -1 h -1 -10.832031 -1 -10.8320312 z m 12.8320312,1 H 31.472656 V 19.261719 H 20.640625 Z M 8.8085938,20.261719 H 19.640625 V 31.09375 H 8.8085938 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,115 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_connected_area.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="5.90625"
inkscape:cy="27.609375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path89"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="m 7.8638887,10.084132 c 0.00101,0.01203 0.00272,0.02557 0.00526,0.04087 l 0.035156,0.203125 2.1367187,12.847656 0.214843,1.289063 0.982422,5.90039 0.0039,0.0293 0.002,0.04492 a 1.1174189,1.1174189 31.375688 0 0 1.274062,0.776949 l 1.309971,-0.08942 0.283199,-0.01758 0.265624,-0.01563 15.117188,-1.078125 a 1.107441,1.107441 135.10954 0 0 1.023438,-1.019532 l 0,0 0.427734,-5.447266 0.02539,-0.322266 0.06055,-0.755859 0.02344,-0.300781 0.789062,-10.052734 0.0059,-0.08203 c 5.2e-5,-6.47e-4 1.04e-4,-0.0013 1.56e-4,-0.0019 0.04173,-0.518787 -0.346547,-0.972695 -0.865395,-1.013686 L 20.908203,10.216797 19.798828,10.128906 8.6777344,9.2421875 C 8.1437829,9.2001186 7.8209269,9.5718807 7.8638887,10.084132 Z m 2.1575953,0.212743 8.246094,0.65625 0.863281,0.06836 -1.660156,2.21875 -0.113281,0.152344 -0.09766,0.128906 -0.128907,0.173828 -0.238281,-0.113281 -3.402344,-1.626953 z m -1.0800778,0.527344 6.3183598,3.021484 1.294922,0.619141 -0.109376,0.146484 -5.623046,7.519531 z m 11.2988278,0.285156 7.986328,0.636719 -9.626953,1.558594 z M 30.886719,12.271484 30.132812,21.875 25.191406,18.59375 19.574219,14.863281 18.654297,14.253906 Z m -12.990235,2.609375 1.208985,0.802735 10.603515,7.042968 -16.998046,7.029297 5.11914,-14.68164 z m -1.455078,1.310547 -4.472656,12.826172 -0.931641,-5.597656 z m 13.552735,7.4375 -0.425782,5.4375 -15.425781,1.119141 z m -17.699219,7.320313 -0.0039,0.0098 v -0.0078 z"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
inkscape:original-d="M 7.7109375,9.1660156 C 8.1218019,10.255395 7.816209,9.8053782 7.8691443,10.125 l 0.035156,0.203125 2.1367187,12.847656 0.214843,1.289063 0.982422,5.90039 0.0039,0.0293 0.002,0.04492 0.152345,0.853515 2.431688,-0.165982 0.283199,-0.01758 0.265624,-0.01563 15.117188,-1.078125 0.949218,-0.06836 0.07422,-0.951172 0.427734,-5.447266 0.02539,-0.322266 0.06055,-0.755859 0.02344,-0.300781 0.789062,-10.052734 0.0059,-0.08203 c 0.02524,-0.313762 0.04951,-0.627607 0.07421,-0.941408 L 30.984458,11.019565 20.908203,10.216797 19.798828,10.128906 8.6777344,9.2421875 Z m 2.3105465,1.1308594 8.246094,0.65625 0.863281,0.06836 -1.660156,2.21875 -0.113281,0.152344 -0.09766,0.128906 -0.128907,0.173828 -0.238281,-0.113281 -3.402344,-1.626953 z m -1.0800778,0.527344 6.3183598,3.021484 1.294922,0.619141 -0.109376,0.146484 -5.623046,7.519531 z m 11.2988278,0.285156 7.986328,0.636719 -9.626953,1.558594 z M 30.886719,12.271484 30.132812,21.875 25.191406,18.59375 19.574219,14.863281 18.654297,14.253906 Z m -12.990235,2.609375 1.208985,0.802735 10.603515,7.042968 -16.998046,7.029297 5.11914,-14.68164 z m -1.455078,1.310547 -4.472656,12.826172 -0.931641,-5.597656 z m 13.552735,7.4375 -0.425782,5.4375 -15.425781,1.119141 z m -17.699219,7.320313 -0.0039,0.0098 v -0.0078 z"
inkscape:path-effect="#path-effect121" />
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -1,121 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_cross.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="16"
inkscape:cx="56.75"
inkscape:cy="8.65625"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" />
<path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" />
<path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" />
<path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" />
<path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" />
<path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" />
<path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" />
<rect
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect3"
width="24.968962"
height="0.96882033"
x="15.762758"
y="-0.50001258"
ry="0"
rx="0.50001299"
transform="rotate(45)" />
<rect
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect4"
width="24.968962"
height="0.96882033"
x="-12.500083"
y="-28.731649"
ry="0.50001299"
rx="0.50001299"
transform="rotate(135)" />
</svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -1,359 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_cut.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="15.375"
inkscape:cy="16.140625"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
d="m 16.082031,8.7625 v 22.475 a 0.965625,0.965625 45 0 0 0.965625,0.965625 h 5.85 A 0.965625,0.965625 135 0 0 23.863281,31.2375 V 8.7625 A 0.965625,0.965625 45 0 0 22.897656,7.796875 h -5.85 A 0.965625,0.965625 135 0 0 16.082031,8.7625 Z m 1.325781,-0.032031 h 5.184376 a 0.3257813,0.3257813 45 0 1 0.325781,0.3257813 V 30.94375 a 0.3257813,0.3257813 135 0 1 -0.325781,0.325781 H 17.407812 A 0.3257813,0.3257813 45 0 1 17.082031,30.94375 V 9.0562501 a 0.3257813,0.3257813 135 0 1 0.325781,-0.3257813 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path33"
inkscape:path-effect="#path-effect34"
inkscape:original-d="m 16.082031,7.796875 v 24.40625 h 7.78125 V 7.796875 Z m 1,0.9335938 h 5.835938 V 31.269531 h -5.835938 z" /><path
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect32"
width="1.0000267"
height="22.719467"
x="27.527292"
y="-12.430544"
transform="rotate(47.337307)"
inkscape:path-effect="#path-effect33"
sodipodi:type="rect"
d="m 28.027306,-12.430544 c 0.277007,0 0.500013,0.223006 0.500013,0.500013 V 9.7889099 c 0,0.2770071 -0.223006,0.5000131 -0.500013,0.5000131 -0.277008,0 -0.500014,-0.223006 -0.500014,-0.5000131 V -11.930531 c 0,-0.277007 0.223006,-0.500013 0.500014,-0.500013 z"
ry="0.50001335" /></svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,197 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_distortion.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect447"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.92418405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.7580314,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.3849613,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5585937,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0.39312043,0,1 @ F,0,0,1,0,0.39312043,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.39312043,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.53242312,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.32768312,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect446"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect445"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5585937,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect229"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,0.49844035,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49844035,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect206"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0051443,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0058593,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0051442,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.003906,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="45.254834"
inkscape:cx="19.489631"
inkscape:cy="18.329534"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path392"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="M 19.810547 7.4824219 A 4.0262158 4.0262158 0 0 0 18.550781 7.7421875 L 10.164062 10.806641 L 9.8007812 10.935547 A 1.2813952 1.2813952 0 0 0 8.9648438 11.9375 L 8.9023438 12.318359 L 8.3183594 15.894531 L 7.8710938 18.640625 A 4.753955 4.753955 0 0 0 7.8085938 19.40625 L 7.8085938 19.746094 L 7.8085938 19.761719 L 7.8085938 20.261719 L 7.8085938 20.642578 L 7.8085938 21.146484 L 7.8085938 26.261719 L 7.8085938 31.09375 L 7.8085938 31.236328 L 7.8085938 31.535156 A 0.5585937 0.5585937 0 0 0 8.3671875 32.09375 L 8.8085938 32.09375 L 18.650391 32.09375 L 19.640625 32.09375 L 20.064453 32.09375 L 20.640625 32.09375 L 31.472656 32.09375 A 1 1 0 0 0 32.472656 31.09375 L 32.472656 26.03125 L 32.472656 21.408203 A 4.849149 4.849149 0 0 0 32.410156 20.626953 L 32.349609 20.261719 L 32.347656 20.261719 L 32.283203 19.873047 L 32.183594 19.261719 L 32.15625 19.085938 L 32.085938 18.658203 L 31.472656 14.904297 L 31.050781 12.318359 L 30.953125 11.714844 A 0.90002024 0.90002024 0 0 0 30.365234 11.011719 L 29.816406 10.816406 L 29.789062 10.806641 L 21.207031 7.7480469 L 20.875 7.625 A 2.3640678 2.3640678 0 0 0 20.240234 7.4882812 A 4.0262158 4.0262158 0 0 0 19.810547 7.4824219 z M 20.640625 8.6074219 L 28.939453 11.564453 L 20.640625 18.652344 L 20.640625 8.6074219 z M 18.722656 8.8183594 L 8.8808594 18.660156 L 9.9785156 11.933594 L 18.722656 8.8183594 z M 19.640625 9.3144531 L 19.640625 19.261719 L 9.6933594 19.261719 L 19.640625 9.3144531 z M 29.984375 11.988281 L 31.171875 19.261719 L 31.173828 19.261719 L 31.216797 19.525391 L 31.335938 20.261719 L 31.333984 20.261719 L 31.214844 19.527344 L 31.171875 19.261719 L 21.466797 19.261719 L 29.984375 11.988281 z M 31.472656 19.261719 L 31.480469 19.261719 L 31.472656 19.269531 L 31.472656 19.261719 z M 8.8085938 20.261719 L 18.783203 20.261719 L 8.8085938 30.236328 L 8.8085938 25.404297 L 8.8085938 20.261719 z M 20.640625 20.261719 L 30.480469 20.261719 L 20.640625 30.103516 L 20.640625 20.261719 z M 31.414062 20.742188 L 31.472656 21.09375 L 31.472656 31.09375 L 21.064453 31.09375 L 31.412109 20.744141 L 31.414062 20.742188 z M 19.640625 20.818359 L 19.640625 31.09375 L 19.650391 31.09375 L 19.640625 31.103516 L 19.640625 31.09375 L 9.3671875 31.09375 L 19.640625 20.818359 z " />
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_fast_preview.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect206"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0051443,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0058593,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0051442,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.003906,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="18.875"
inkscape:cy="8.734375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path150"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="m 10.849609,11.300781 -0.208984,2.06836 -0.101563,1 -0.259765,2.546875 -0.101563,1 -0.2695309,2.664062 -0.1015625,1 -0.2714844,2.66211 -0.1015624,1 -0.2578126,2.546874 -0.1015624,1 -0.2558594,2.507813 a 0.90855311,0.90855311 47.877062 0 0 0.9042968,1 h 0 3.9492188 1.001953 4.632813 1 4.964843 1.001954 4.021484 l 7.15e-4,0 a 0.90823039,0.90823039 132.10041 0 0 0.903582,-1 l -0.25586,-2.507813 -0.101562,-1 -0.257813,-2.546874 -0.101562,-1 -0.271484,-2.66211 -0.101563,-1 -0.269531,-2.664062 -0.101563,-1 -0.259765,-2.546875 -0.101563,-1 -0.210937,-2.06836 -1.25e-4,-0.0012 a 1.1110301,1.1110301 42.100377 0 0 -1.105344,-0.998768 h -3.390625 -1.003906 -3.359375 -1 -3.027344 -1.001953 -3.320313 -7.15e-4 a 1.1124005,1.1124005 137.89962 0 0 -1.106707,1 z m 1.00586,0 h 3.349609 l -0.152344,2.06836 h -3.40625 z m 4.351562,0 h 3.09961 1 3.43164 l 0.152344,2.06836 h -3.583984 -1 -3.251953 z m 8.535157,0 h 3.419921 l 0.210938,2.06836 h -3.480469 z m -13.197266,3.06836 h 3.435547 1.001953 3.324219 v 2.546875 h -3.509766 -1.001953 -3.509766 z m 8.761719,0 h 3.65625 1.003906 3.507812 l 0.257813,2.546875 h -3.580078 -1.003906 -3.841797 z m -9.123047,3.546875 h 3.537109 l -0.193359,2.664062 h -3.615235 z m 4.541015,0 h 3.582032 1 3.914062 l 0.195313,2.664062 h -4.109375 -1 -3.777344 z m 9.5,0 h 3.609375 l 0.271485,2.664062 h -3.685547 z m -14.414062,3.664062 h 3.642578 1.003906 3.84961 v 2.66211 h -4.044922 -1.001953 -3.71875 z m 9.496094,0 h 4.18164 1.003907 3.714843 l 0.269531,2.66211 h -3.791015 -1.001953 -4.376953 z m -9.867188,3.66211 h 3.746094 L 14,27.789062 h -3.820312 z m 4.75,0 h 4.117188 1 4.449218 l 0.185547,2.546874 h -4.634765 -1 -4.302735 z m 10.570313,0 h 3.818359 l 0.257813,2.546874 h -3.890626 z m -15.681641,3.546874 h 3.849609 1.001954 4.376953 v 2.507813 H 14.748047 13.744141 9.8242188 Z m 10.228516,0 h 4.708984 1.001953 3.919922 l 0.255859,2.507813 h -3.992187 -1.003906 -4.890625 z"
inkscape:path-effect="#path-effect206"
inkscape:original-d="m 10.951172,10.300781 -0.101563,1 -0.208984,2.06836 -0.101563,1 -0.259765,2.546875 -0.101563,1 -0.2695309,2.664062 -0.1015625,1 -0.2714844,2.66211 -0.1015624,1 -0.2578126,2.546874 -0.1015624,1 -0.2558594,2.507813 -0.1015625,1 h 1.0058593 3.9492188 1.001953 4.632813 1 4.964843 1.001954 4.021484 1.005859 l -0.101562,-1 -0.25586,-2.507813 -0.101562,-1 -0.257813,-2.546874 -0.101562,-1 -0.271484,-2.66211 -0.101563,-1 -0.269531,-2.664062 -0.101563,-1 -0.259765,-2.546875 -0.101563,-1 -0.210937,-2.06836 -0.101563,-1 h -1.003906 -3.390625 -1.003906 -3.359375 -1 -3.027344 -1.001953 -3.320313 z m 0.904297,1 h 3.349609 l -0.152344,2.06836 h -3.40625 z m 4.351562,0 h 3.09961 1 3.43164 l 0.152344,2.06836 h -3.583984 -1 -3.251953 z m 8.535157,0 h 3.419921 l 0.210938,2.06836 h -3.480469 z m -13.197266,3.06836 h 3.435547 1.001953 3.324219 v 2.546875 h -3.509766 -1.001953 -3.509766 z m 8.761719,0 h 3.65625 1.003906 3.507812 l 0.257813,2.546875 h -3.580078 -1.003906 -3.841797 z m -9.123047,3.546875 h 3.537109 l -0.193359,2.664062 h -3.615235 z m 4.541015,0 h 3.582032 1 3.914062 l 0.195313,2.664062 h -4.109375 -1 -3.777344 z m 9.5,0 h 3.609375 l 0.271485,2.664062 h -3.685547 z m -14.414062,3.664062 h 3.642578 1.003906 3.84961 v 2.66211 h -4.044922 -1.001953 -3.71875 z m 9.496094,0 h 4.18164 1.003907 3.714843 l 0.269531,2.66211 h -3.791015 -1.001953 -4.376953 z m -9.867188,3.66211 h 3.746094 L 14,27.789062 h -3.820312 z m 4.75,0 h 4.117188 1 4.449218 l 0.185547,2.546874 h -4.634765 -1 -4.302735 z m 10.570313,0 h 3.818359 l 0.257813,2.546874 h -3.890626 z m -15.681641,3.546874 h 3.849609 1.001954 4.376953 v 2.507813 H 14.748047 13.744141 9.8242188 Z m 10.228516,0 h 4.708984 1.001953 3.919922 l 0.255859,2.507813 h -3.992187 -1.003906 -4.890625 z"
transform="translate(-0.03578843,-1.298844)" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,242 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_frame.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="16"
inkscape:cx="11.6875"
inkscape:cy="6.28125"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><g
id="g5-0"
transform="translate(6.4998844,6.0000382)"
style="fill:#ff2323;fill-opacity:1"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4-1"
style="fill:#ff2323;fill-opacity:1" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5-2"
style="fill:#4e2ca6;fill-opacity:1;fill-rule:nonzero" /></g><g
id="g5-0-8"
transform="matrix(-1,0,0,1,33.44613,6.0000382)"
style="fill:#ff2323;fill-opacity:1"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4-1-3"
style="fill:#ff2323;fill-opacity:1" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5-2-5"
style="fill:#ff2323;fill-opacity:1" /></g><g
id="g5-0-8-2"
transform="rotate(180,16.723064,16.999981)"
style="fill:#ff2323;fill-opacity:1"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4-1-3-0"
style="fill:#ff2323;fill-opacity:1" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5-2-5-1"
style="fill:#ff2323;fill-opacity:1" /></g><g
id="g5-0-8-2-4"
transform="matrix(1,0,0,-1,6.4998854,33.999963)"
style="fill:#ff2323;fill-opacity:1"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4-1-3-0-0"
style="fill:#ff2323;fill-opacity:1" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5-2-5-1-6"
style="fill:#ff2323;fill-opacity:1" /></g><path
id="path13"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
d="m 10.972656,11.5 v 4.101562 1.407813 A 0.490625,0.490625 45 0 0 11.463281,17.5 h 0.0094 a 0.4999428,0.4999428 135 0 0 0.499943,-0.499943 V 15.601562 12.000236 A 0.5002363,0.5002363 135 0 1 12.472892,11.5 h 0.241952 0.503906 4.255468 a 0.4984375,0.4984375 135 0 0 0.498438,-0.498437 v -0.0018 A 0.4997516,0.4997516 45 0 0 17.472904,10.5 H 13.21875 12.714844 11.972656 a 1,1 135 0 0 -1,1 z"
inkscape:original-d="m 10.972656,10.5 v 1 4.101562 V 17.5 h 1 V 15.601562 11.5 h 0.742188 0.503906 4.753906 v -1 H 13.21875 12.714844 11.972656 Z"
inkscape:path-effect="#path-effect15" /><path
id="path16"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
d="m 10.972656,11.5 v 4.101562 1.439063 A 0.459375,0.459375 45 0 0 11.432031,17.5 h 0.04066 a 0.499968,0.499968 135 0 0 0.499968,-0.499968 l 0,-1.39847 V 12.000236 A 0.5002363,0.5002363 135 0 1 12.472892,11.5 h 0.241952 0.503906 4.271094 a 0.4828125,0.4828125 135 0 0 0.482812,-0.482812 V 11.00584 A 0.50584,0.50584 45 0 0 17.466816,10.5 H 13.21875 12.714844 11.972656 a 1,1 135 0 0 -1,1 z"
inkscape:original-d="m 10.972656,10.5 v 1 4.101562 V 17.5 h 1 V 15.601562 11.5 h 0.742188 0.503906 4.753906 v -1 H 13.21875 12.714844 11.972656 Z"
inkscape:path-effect="#path-effect17"
transform="matrix(-1,0,0,1,39.939926,2.52e-5)" /><path
id="path17"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
d="m 10.972656,11.5 v 4.101562 1.439063 A 0.459375,0.459375 45 0 0 11.432031,17.5 h 0.04066 a 0.499968,0.499968 135 0 0 0.499968,-0.499968 l 0,-1.39847 V 12.000236 A 0.5002363,0.5002363 135 0 1 12.472892,11.5 h 0.241952 0.503906 4.271094 a 0.4828125,0.4828125 135 0 0 0.482812,-0.482812 V 11.00584 A 0.50584,0.50584 45 0 0 17.466816,10.5 H 13.21875 12.714844 11.972656 a 1,1 135 0 0 -1,1 z"
inkscape:original-d="m 10.972656,10.5 v 1 4.101562 V 17.5 h 1 V 15.601562 11.5 h 0.742188 0.503906 4.753906 v -1 H 13.21875 12.714844 11.972656 Z"
inkscape:path-effect="#path-effect19"
transform="rotate(180,19.972885,20.000096)" /><path
id="path19"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
d="m 10.972656,11.5 v 4.101562 1.439063 A 0.459375,0.459375 45 0 0 11.432031,17.5 h 0.04066 a 0.499968,0.499968 135 0 0 0.499968,-0.499968 l 0,-1.39847 V 12.000236 A 0.5002363,0.5002363 135 0 1 12.472892,11.5 h 0.241952 0.503906 4.271094 a 0.4828125,0.4828125 135 0 0 0.482812,-0.482812 V 11.00584 A 0.50584,0.50584 45 0 0 17.466816,10.5 H 13.21875 12.714844 11.972656 a 1,1 135 0 0 -1,1 z"
inkscape:original-d="m 10.972656,10.5 v 1 4.101562 V 17.5 h 1 V 15.601562 11.5 h 0.742188 0.503906 4.753906 v -1 H 13.21875 12.714844 11.972656 Z"
inkscape:path-effect="#path-effect21"
transform="matrix(1,0,0,-1,0.0060898,39.99995)" /></svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,387 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_join.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35-7"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="18.3125"
inkscape:cy="22.328125"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
id="rect35"
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.74375,1 h 7.563281 a 0.24375,0.24375 45 0 1 0.24375,0.24375 v 7.575 a 0.24375,0.24375 135 0 1 -0.24375,0.24375 h -7.563281 a 0.24375,0.24375 45 0 1 -0.24375,-0.24375 v -7.575 a 0.24375,0.24375 135 0 1 0.24375,-0.24375 z"
inkscape:path-effect="#path-effect35"
inkscape:original-d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.5,1 h 8.050781 v 8.0625 h -8.050781 z"
transform="translate(0.294922,2.953125)" /><path
id="rect35-3"
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.74375,1 h 7.563281 a 0.24375,0.24375 45 0 1 0.24375,0.24375 v 7.575 a 0.24375,0.24375 135 0 1 -0.24375,0.24375 h -7.563281 a 0.24375,0.24375 45 0 1 -0.24375,-0.24375 v -7.575 a 0.24375,0.24375 135 0 1 0.24375,-0.24375 z"
inkscape:path-effect="#path-effect35-7"
inkscape:original-d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.5,1 h 8.050781 v 8.0625 h -8.050781 z"
transform="translate(9.345703,2.953125)" /></svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_real_preview.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect229"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,0.49844035,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49844035,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect206"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0051443,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0058593,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0051442,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.003906,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="22.627417"
inkscape:cx="25.279067"
inkscape:cy="19.202368"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path227"
style="fill:#009688;fill-opacity:1;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="m 9.0234375,12.297268 v 0.499607 0.002 0.002 V 28.5 v 1 h 1.0000005 19.925781 1 v -1 -14.027344 -1.673828 -0.516008 c 0,-0.267301 -0.216678,-0.487988 -0.483974,-0.487257 -0.01082,3e-5 -0.02151,6.6e-5 -0.03209,1.08e-4 -0.267272,0.0011 -0.483932,0.219848 -0.483932,0.487149 v 0.516008 1.359375 c -0.414107,0.519735 -1.042494,0.822425 -1.707031,0.822266 -1.124527,-0.0023 -2.063124,-0.858885 -2.167969,-1.978516 -0.0077,-0.06746 -0.01228,-0.135241 -0.01367,-0.203125 1.13e-4,-0.08617 0.0053,-0.172259 0.01563,-0.257812 -0.0105,-0.134093 -0.02949,-0.267265 -0.0568,-0.398724 -0.03994,-0.192221 -0.250872,-0.343464 -0.4473,-0.343464 -0.192979,0 -0.401264,0.151193 -0.44139,0.343374 -0.02852,0.136589 -0.04805,0.275032 -0.05841,0.414439 0.0097,0.08038 0.01491,0.161231 0.01563,0.242187 -0.001,0.0718 -0.0056,0.143498 -0.01367,0.214844 -0.110484,1.114992 -1.047518,1.965076 -2.167969,1.966797 -1.204815,-1.73e-4 -2.181468,-0.976826 -2.181641,-2.181641 -1.87e-4,-0.221094 -0.02342,-0.441241 -0.06912,-0.656906 -0.04069,-0.192058 -0.25223,-0.343094 -0.448658,-0.343094 -0.177877,0 -0.374076,0.151139 -0.414397,0.34328 -0.04525,0.215649 -0.06805,0.435732 -0.06783,0.65672 -1.73e-4,1.204815 -0.976825,2.181468 -2.18164,2.181641 -1.120451,-0.0017 -2.057485,-0.851805 -2.167969,-1.966797 -0.0081,-0.07135 -0.01265,-0.143048 -0.01367,-0.214844 1.13e-4,-0.08617 0.0053,-0.172259 0.01563,-0.257812 -0.01085,-0.134196 -0.03019,-0.267448 -0.05787,-0.398962 -0.04043,-0.192117 -0.251758,-0.343226 -0.448186,-0.343226 -0.192979,0 -0.400882,0.151323 -0.440519,0.343606 -0.02815,0.136537 -0.04732,0.274902 -0.05733,0.414207 0.0097,0.08038 0.01491,0.161231 0.01563,0.242187 -0.0014,0.06788 -0.006,0.135666 -0.01367,0.203125 -0.104845,1.119631 -1.043442,1.976207 -2.167969,1.978516 -1.205578,9.06e-4 -2.18342,-0.976063 -2.183593,-2.181641 v -0.50156 a 0.49844035,0.49844035 45 0 0 -0.4984403,-0.49844 h -0.00315 a 0.49844035,0.49844035 135 0 0 -0.4984403,0.49844 z m 11.2011715,2.204685 c 0.582376,0.918834 1.593793,1.476477 2.681641,1.478516 1.079194,-0.0024 2.083602,-0.551691 2.667969,-1.458985 0.584367,0.907294 1.588775,1.456557 2.667969,1.458985 0.604585,1.64e-4 1.196705,-0.171917 1.707031,-0.496094 V 28.5 H 10.023438 V 15.115234 c 0.590891,0.556323 1.372022,0.865842 2.183593,0.865235 1.079194,-0.0024 2.083602,-0.551691 2.667969,-1.458985 0.584367,0.907294 1.588775,1.456557 2.667969,1.458985 1.087848,-0.002 2.099265,-0.559682 2.68164,-1.478516 z"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
inkscape:path-effect="#path-effect229"
inkscape:original-d="m 9.0234375,11.798828 v 0.998047 0.002 0.002 V 28.5 v 1 h 1.0000005 19.925781 1 v -1 -14.027344 -1.673828 -1 c -0.473966,-0.0071 -0.666658,-8.7e-4 -1,0 v 1 1.359375 c -0.414107,0.519735 -1.042494,0.822425 -1.707031,0.822266 -1.124527,-0.0023 -2.063124,-0.858885 -2.167969,-1.978516 -0.0077,-0.06746 -0.01228,-0.135241 -0.01367,-0.203125 1.13e-4,-0.08617 0.0053,-0.172259 0.01563,-0.257812 -0.01977,-0.252421 -0.06961,-0.501579 -0.148438,-0.742188 h -0.705078 c -0.08072,0.245558 -0.131226,0.500038 -0.15039,0.757813 0.0097,0.08038 0.01491,0.161231 0.01563,0.242187 -0.001,0.0718 -0.0056,0.143498 -0.01367,0.214844 -0.110484,1.114992 -1.047518,1.965076 -2.167969,1.966797 -1.204815,-1.73e-4 -2.181468,-0.976826 -2.181641,-2.181641 -2.87e-4,-0.339851 -0.05502,-0.677463 -0.162109,-1 H 19.88478 c -0.106429,0.322646 -0.1605,0.660254 -0.160157,1 -1.73e-4,1.204815 -0.976825,2.181468 -2.18164,2.181641 -1.120451,-0.0017 -2.057485,-0.851805 -2.167969,-1.966797 -0.0081,-0.07135 -0.01265,-0.143048 -0.01367,-0.214844 1.13e-4,-0.08617 0.0053,-0.172259 0.01563,-0.257812 -0.02041,-0.2525 -0.0709,-0.50166 -0.150391,-0.742188 h -0.705078 c -0.08006,0.245641 -0.129907,0.500119 -0.148437,0.757813 0.0097,0.08038 0.01491,0.161231 0.01563,0.242187 -0.0014,0.06788 -0.006,0.135666 -0.01367,0.203125 -0.104845,1.119631 -1.043442,1.976207 -2.167969,1.978516 -1.205578,9.06e-4 -2.18342,-0.976063 -2.183593,-2.181641 v -1 z m 11.2011715,2.703125 c 0.582376,0.918834 1.593793,1.476477 2.681641,1.478516 1.079194,-0.0024 2.083602,-0.551691 2.667969,-1.458985 0.584367,0.907294 1.588775,1.456557 2.667969,1.458985 0.604585,1.64e-4 1.196705,-0.171917 1.707031,-0.496094 V 28.5 H 10.023438 V 15.115234 c 0.590891,0.556323 1.372022,0.865842 2.183593,0.865235 1.079194,-0.0024 2.083602,-0.551691 2.667969,-1.458985 0.584367,0.907294 1.588775,1.456557 2.667969,1.458985 1.087848,-0.002 2.099265,-0.559682 2.68164,-1.478516 z"
transform="translate(0.26516504,0.08838835)" />
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,260 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_snap.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="45.254834"
inkscape:cx="14.363106"
inkscape:cy="12.827359"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
d="M 20.422985,9.2501548 V 29.968905 H 28.78236 A 1.015625,1.015625 135 0 0 29.797985,28.95328 V 9.2501548 A 1.015625,1.015625 45 0 0 28.78236,8.2345298 h -7.34375 a 1.015625,1.015625 135 0 0 -1.015625,1.015625 z m 1.015625,-0.015625 h 7.34375 V 28.968905 h -7.34375 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path23"
inkscape:path-effect="#path-effect24"
inkscape:original-d="M 20.422985,8.2345298 V 29.968905 h 9.375 V 8.2345298 Z m 1.015625,1 h 7.34375 V 28.968905 h -7.34375 z" /><path
d="m 11.047985,11.324808 v 19.616307 a 1.059034,1.059034 45 0 0 1.059034,1.059034 h 7.256932 a 1.059034,1.059034 135 0 0 1.059034,-1.059034 V 10.265774 h -8.315966 a 1.059034,1.059034 135 0 0 -1.059034,1.059034 z m 1.015625,-0.05903 h 7.34375 v 19.734375 h -7.34375 z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path23-8"
inkscape:path-effect="#path-effect25"
inkscape:original-d="m 11.047985,10.265774 v 21.734375 h 9.375 V 10.265774 Z m 1.015625,1 h 7.34375 v 19.734375 h -7.34375 z" /><path
id="use25"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
d="M 21.439453 8.234375 A 1.015625 1.015625 0 0 0 20.423828 9.25 L 20.423828 10.265625 L 12.107422 10.265625 A 1.059034 1.059034 0 0 0 11.048828 11.324219 L 11.048828 30.941406 A 1.059034 1.059034 0 0 0 12.107422 32 L 19.363281 32 A 1.059034 1.059034 0 0 0 20.423828 30.941406 L 20.423828 29.96875 L 28.783203 29.96875 A 1.015625 1.015625 0 0 0 29.798828 28.953125 L 29.798828 9.25 A 1.015625 1.015625 0 0 0 28.783203 8.234375 L 21.439453 8.234375 z M 21.439453 9.234375 L 28.783203 9.234375 L 28.783203 28.96875 L 21.439453 28.96875 L 21.439453 9.234375 z M 12.064453 11.265625 L 19.408203 11.265625 L 19.408203 31 L 12.064453 31 L 12.064453 11.265625 z " /></svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,387 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_unjoin.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35-7"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="18.3125"
inkscape:cy="22.328125"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
id="rect35"
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.74375,1 h 7.563281 a 0.24375,0.24375 45 0 1 0.24375,0.24375 v 7.575 a 0.24375,0.24375 135 0 1 -0.24375,0.24375 h -7.563281 a 0.24375,0.24375 45 0 1 -0.24375,-0.24375 v -7.575 a 0.24375,0.24375 135 0 1 0.24375,-0.24375 z"
inkscape:path-effect="#path-effect35"
inkscape:original-d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.5,1 h 8.050781 v 8.0625 h -8.050781 z"
transform="translate(-1.142612,2.9530855)" /><path
id="rect35-3"
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.74375,1 h 7.563281 a 0.24375,0.24375 45 0 1 0.24375,0.24375 v 7.575 a 0.24375,0.24375 135 0 1 -0.24375,0.24375 h -7.563281 a 0.24375,0.24375 45 0 1 -0.24375,-0.24375 v -7.575 a 0.24375,0.24375 135 0 1 0.24375,-0.24375 z"
inkscape:path-effect="#path-effect35-7"
inkscape:original-d="m 11.154297,12.015625 c -0.277007,0 -0.5,0.222993 -0.5,0.5 v 9.050781 c 0,0.0019 -2.2e-5,0.0039 0,0.0059 -2.3e-5,0.002 0,0.0039 0,0.0059 3.45e-4,0.01513 0.0023,0.03016 0.0039,0.04492 0.02282,0.255546 0.234414,0.455078 0.496094,0.455078 h 9.050781 c 0.277007,0 0.5,-0.222993 0.5,-0.5 v -0.002 -9.048828 c 9e-5,-0.0039 0,-0.0078 0,-0.01172 0,-0.277007 -0.222993,-0.5 -0.5,-0.5 z m 0.5,1 h 8.050781 v 8.0625 h -8.050781 z"
transform="translate(10.783203,2.9530855)" /></svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,383 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_uv_select_edge.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35-7"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="13.65625"
inkscape:cy="21.984375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><rect
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect35"
width="1.0000261"
height="23.343758"
x="19.972986"
y="8.3281212"
rx="0.50001299"
ry="0.50001299" /></svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,413 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_uv_select_island.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect37"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.22141448,0,1 @ F,0,1,1,0,0.22141448,0,1 @ F,0,1,1,0,0.22141448,0,1 @ F,0,0,1,0,0.22141448,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect36"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35-7"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="13.65625"
inkscape:cy="21.984375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
id="rect35"
style="fill:#009688;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 9.5039062,8.65625 c -0.2770071,0 -0.5,0.2229928 -0.5,0.5 V 31.484375 31.5 c 3.682e-4,0.0119 7.748e-4,0.02348 0.00195,0.03516 C 9.0240314,31.795393 9.2388928,32 9.5039062,32 c 0.043378,0 0.08492,-0.0053 0.125,-0.01563 H 31.722656 c 0.04008,0.01029 0.08162,0.01563 0.125,0.01563 0.265014,0 0.479875,-0.204607 0.498047,-0.464844 0.0017,-0.01685 0.002,-0.03347 0.002,-0.05078 V 9.15625 c 0,-0.2770072 -0.222993,-0.5 -0.5,-0.5 z m 0.7214138,1 20.900922,0 a 0.22141448,0.22141448 45 0 1 0.221414,0.2214145 l 0,20.8852965 a 0.22141448,0.22141448 135 0 1 -0.221414,0.221414 l -20.900922,0 a 0.22141448,0.22141448 45 0 1 -0.221414,-0.221414 l 0,-20.8852965 A 0.22141448,0.22141448 135 0 1 10.22532,9.65625 Z"
inkscape:path-effect="#path-effect37"
inkscape:original-d="m 9.5039062,8.65625 c -0.2770071,0 -0.5,0.2229928 -0.5,0.5 V 31.484375 31.5 c 3.682e-4,0.0119 7.748e-4,0.02348 0.00195,0.03516 C 9.0240314,31.795393 9.2388928,32 9.5039062,32 c 0.043378,0 0.08492,-0.0053 0.125,-0.01563 H 31.722656 c 0.04008,0.01029 0.08162,0.01563 0.125,0.01563 0.265014,0 0.479875,-0.204607 0.498047,-0.464844 0.0017,-0.01685 0.002,-0.03347 0.002,-0.05078 V 9.15625 c 0,-0.2770072 -0.222993,-0.5 -0.5,-0.5 z m 0.4999998,1 h 21.34375 v 21.328125 h -21.34375 z"
transform="translate(-0.5,-0.32812498)" /></svg>

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,378 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_uv_select_vertex.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs17"><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 @ F,0,0,1,0,0.965625,0,1 | F,0,0,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1 @ F,0,1,1,0,0.3257813,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.2615305,0,1 @ F,0,0,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 @ F,0,1,1,0,1.2615305,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect27"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect25"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,1.059034,0,1 @ F,0,1,1,0,1.059034,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect24"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,1.015625,0,1 @ F,0,0,1,0,1.015625,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect21"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect19"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect17"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.459375,0,1 @ F,0,0,1,0,0.499968,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.4828125,0,1 @ F,0,0,1,0,0.50584,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect15"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5002363,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><linearGradient
id="swatch7"
inkscape:swatch="solid"><stop
style="stop-color:#77eaa3;stop-opacity:1;"
offset="0"
id="stop7" /></linearGradient><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.49883385,0,1 @ F,0,0,1,0,0.48672135,0,1 @ F,0,0,1,0,0.5261776,0,1 @ F,0,0,1,0,0.50001454,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31-9-1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 @ F,0,0,1,0,1.2263883,0,1 | F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1 @ F,0,1,1,0,0.47508735,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /><inkscape:path-effect
effect="fillet_chamfer"
id="path-effect35-7"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,1,1,0,0.24375,0,1 @ F,0,0,1,0,0.24375,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" /></defs><sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="16.6875"
inkscape:cy="29.109375"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" /><path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" /><path
d="m 35.473026,33.999994 v -0.999987 a 0.500013,0.500013 0 0 0 -1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 1 -0.499993,0.499994 h -5.000013 a 0.5000126,0.5000126 0 0 0 0,1.000025 h 5.000013 a 1.5,1.5 0 0 0 1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7" /><path
d="m 5.9730079,35.500013 h 0.999987 a 0.500013,0.500013 0 0 0 0,-1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 1 -0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 0 -1.000025,0 v 5.000013 a 1.5,1.5 0 0 0 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3" /><path
d="m 4.4729879,33.999994 v -0.999987 a 0.500013,0.500013 0 0 1 1.000026,0 v 0.999987 a 0.49999998,0.49999998 0 0 0 0.499993,0.499994 H 10.97302 a 0.5000126,0.5000126 0 0 1 0,1.000025 H 5.9730069 a 1.5,1.5 0 0 1 -1.500019,-1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5" /><path
d="m 5.9730079,4.499987 h 0.999987 a 0.50001302,0.50001302 0 0 1 0,1.000026 h -0.999987 a 0.49999998,0.49999998 0 0 0 -0.499994,0.499993 v 5.000013 a 0.5000126,0.5000126 0 0 1 -1.0000251,0 V 6.000006 A 1.5,1.5 0 0 1 5.9730079,4.499987 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9" /><path
d="m 4.4729878,6.000006 v 0.999987 a 0.50001302,0.50001302 0 0 0 1.0000261,0 V 6.000006 A 0.49999998,0.49999998 0 0 1 5.9730068,5.500012 H 10.97302 a 0.5000126,0.5000126 0 0 0 0,-1.000025 H 5.9730068 a 1.5,1.5 0 0 0 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8" /><path
d="m 33.973006,4.499986 h -0.999987 a 0.50001302,0.50001302 0 0 0 0,1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 1 34.473,6.000005 v 5.000013 a 0.5000126,0.5000126 0 0 0 1.000025,0 V 6.000005 A 1.5,1.5 0 0 0 33.973006,4.499986 Z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-3-9-8" /><path
d="m 35.473026,6.000005 v 0.999987 a 0.50001305,0.50001305 0 0 1 -1.000026,0 V 6.000005 A 0.49999998,0.49999998 0 0 0 33.973007,5.500011 h -5.000013 a 0.5000126,0.5000126 0 0 1 0,-1.000025 h 5.000013 a 1.5,1.5 0 0 1 1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6-7-5-8-2" /><g
id="g5"><use
x="0"
y="0"
xlink:href="#path3-6-3-9"
id="use4" /><use
x="0"
y="0"
xlink:href="#path3-6-7-5-8"
id="use5" /></g><path
d="M 20.472999,14.75 A 5.2386374,5.25 0 0 0 15.234718,20 5.2386374,5.25 0 0 0 20.472999,25.25 5.2386374,5.25 0 0 0 25.71128,20 5.2386374,5.25 0 0 0 20.472999,14.75 Z m 0,0.974609 A 4.265625,4.2748771 0 0 1 24.738624,20 4.265625,4.2748771 0 0 1 20.472999,24.275391 4.265625,4.2748771 0 0 1 16.207374,20 4.265625,4.2748771 0 0 1 20.472999,15.724609 Z"
style="fill:#009688;stroke-width:0.1;paint-order:stroke fill markers"
id="path39" /></svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,163 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="texture_displacement_wireframe.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect294"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.636718,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect229"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,1,1,0,0.49844035,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0.48399193,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0.35566405,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49844035,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect206"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0051443,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0058593,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0051442,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.003906,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect121"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0.97194886,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.9540633,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.94238026,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="26.09375"
inkscape:cy="31.421875"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path291"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="M 8.8085938 7.4296875 A 1 1 0 0 0 7.8085938 8.4296875 L 7.8085938 19.261719 L 7.8085938 20.261719 L 7.8085938 31.09375 A 1 1 0 0 0 8.8085938 32.09375 L 18.796875 32.09375 L 19.640625 32.09375 L 20.210938 32.09375 L 20.640625 32.09375 L 31.472656 32.09375 A 1 1 0 0 0 32.472656 31.09375 L 32.472656 20.261719 L 32.472656 19.830078 L 32.472656 19.261719 L 32.472656 18.416016 L 32.472656 8.4296875 L 32.472656 8.2070312 L 32.472656 8.0664062 A 0.636718 0.636718 0 0 0 31.835938 7.4296875 L 31.472656 7.4296875 L 20.640625 7.4296875 L 19.640625 7.4296875 L 8.8085938 7.4296875 z M 20.640625 8.4296875 L 30.835938 8.4296875 L 20.640625 18.626953 L 20.640625 8.4296875 z M 31.472656 9.2070312 L 31.472656 19.261719 L 21.419922 19.261719 L 31.472656 9.2070312 z M 20.640625 20.261719 L 30.626953 20.261719 L 20.640625 30.25 L 20.640625 20.261719 z M 31.472656 20.830078 L 31.472656 31.09375 L 21.210938 31.09375 L 31.472656 20.830078 z " />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="toolbar_big_brush.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17" />
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="5.6568543"
inkscape:cx="-13.081475"
inkscape:cy="98.994949"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<path
id="path30"
style="fill:#009688;fill-opacity:1;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers"
d="M 30.109375 6.9648438 A 1.8710937 1.8710937 0 0 0 28.705078 7.5996094 A 1.8710937 1.8710937 0 0 0 28.591797 7.7421875 L 18.103516 20.615234 A 6.1070809 6.1070809 0 0 0 17.226562 20.552734 A 6.1070809 6.1070809 0 0 0 11.513672 24.503906 A 6.1070809 6.1070809 0 0 0 11.121094 26.658203 A 6.1070809 6.1070809 0 0 0 11.121094 26.757812 A 4.4931102 4.4931102 0 0 1 11.15625 27.3125 A 4.4931102 4.4931102 0 0 1 7.7890625 31.662109 A 4.4931102 4.4931102 0 0 1 7.5839844 31.708984 A 0.55609465 0.55242634 0 0 0 7.2558594 32.212891 A 0.55609465 0.55242634 0 0 0 7.8105469 32.765625 A 0.55609465 0.55242634 0 0 0 7.8125 32.765625 L 7.9570312 32.765625 L 17.224609 32.765625 L 17.226562 32.765625 A 6.1070809 6.1070809 0 0 0 23.333984 26.658203 A 6.1070809 6.1070809 0 0 0 22.792969 24.144531 L 31.666016 9.875 A 1.8710937 1.8710937 0 0 1 31.638672 9.9101562 A 1.8710937 1.8710937 0 0 0 31.699219 9.8242188 A 1.8710937 1.8710937 0 0 0 31.980469 8.8359375 A 1.8710937 1.8710937 0 0 0 31.724609 7.890625 A 1.8710937 1.8710937 0 0 0 30.480469 7.0019531 A 1.8710937 1.8710937 0 0 0 30.109375 6.9648438 z M 28.525391 7.8476562 A 1.8710937 1.8710937 0 0 0 28.472656 7.9335938 A 1.8710937 1.8710937 0 0 1 28.478516 7.9199219 A 1.8710937 1.8710937 0 0 1 28.525391 7.8476562 z M 30.109375 7.9570312 A 0.87817276 0.87817276 0 0 1 30.988281 8.8359375 A 0.87817276 0.87817276 0 0 1 30.90625 9.2050781 A 0.87817276 0.87817276 0 0 1 30.791016 9.390625 L 29.974609 10.703125 L 22.230469 23.15625 A 6.1070809 6.1070809 0 0 0 19.183594 20.873047 L 28.388672 9.5703125 L 29.359375 8.3808594 A 0.87817276 0.87817276 0 0 1 29.511719 8.1933594 A 0.87817276 0.87817276 0 0 1 30.109375 7.9570312 z M 28.457031 7.9628906 A 1.8710937 1.8710937 0 0 0 28.373047 8.1425781 A 1.8710937 1.8710937 0 0 1 28.457031 7.9628906 z M 28.300781 8.359375 A 1.8710937 1.8710937 0 0 0 28.263672 8.5507812 A 1.8710937 1.8710937 0 0 1 28.298828 8.3613281 A 1.8710937 1.8710937 0 0 1 28.300781 8.359375 z M 17.226562 21.65625 A 5.0022264 5.0022264 0 0 1 22.230469 26.658203 A 5.0022264 5.0022264 0 0 1 17.226562 31.662109 L 13.724609 31.662109 L 10.199219 31.662109 A 5.6057596 5.6057596 0 0 0 11.822266 29.501953 A 5.6057596 5.6057596 0 0 0 12.267578 27.3125 A 5.0022264 5.0022264 0 0 1 12.224609 26.658203 A 5.0022264 5.0022264 0 0 1 12.224609 26.621094 A 5.0022264 5.0022264 0 0 1 17.226562 21.65625 z " />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="toolbar_face.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect48"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,1.0055868,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.4911902,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0376412,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 | F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect47"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="22.627417"
inkscape:cx="23.113553"
inkscape:cy="18.318485"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<path
d="m 33.973006,35.500013 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 A 0.49999998,0.49999998 0 0 0 34.473,33.999994 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.973012,35.500013 H 5.9730109 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 6.000006 a 1.5,1.5 0 0 1 1.5,-1.500019 H 33.973006 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 6.000006 A 0.49999998,0.49999998 0 0 0 33.973006,5.500012 H 5.9730109 a 0.49999998,0.49999998 0 0 0 -0.499997,0.499994 v 27.999988 a 0.49999998,0.49999998 0 0 0 0.499997,0.499993 H 19.973012 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999;fill-opacity:1"
id="path4-6" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect40"
width="15.363631"
height="1.000026"
x="18.609375"
y="34.499989" />
<rect
style="fill:#b6b6b6;fill-opacity:1;stroke:none;stroke-width:0;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;paint-order:stroke fill markers"
id="rect41"
width="1.000025"
height="11.921875"
x="34.473"
y="18.546875" />
<path
id="path42"
style="fill:#009688;stroke-width:0;stroke-opacity:0;paint-order:stroke fill markers;fill-opacity:1"
d="M 19.58431,7.9401469 19.519531,8.0820312 9.1699219,30.736328 a 0.61329334,0.61329334 42.827026 0 0 0.828125,0.767578 l 0,0 20.7832031,-7.320312 0.0019,-6.61e-4 a 0.7874119,0.7874119 107.77086 0 0 0.382891,-1.194652 L 20.714844,8.0449219 20.578195,7.8494958 A 0.57476577,0.57476577 174.78854 0 0 19.58431,7.9401469 Z M 20.232422,9.1015625 30.1875,23.332031 10.576172,30.240234 Z"
inkscape:path-effect="#path-effect48"
inkscape:original-d="M 20.001953,7.0253906 19.519531,8.0820312 9.1699219,30.736328 8.5917969,32 9.9980469,31.503906 30.78125,24.183594 31.761719,23.837891 31.166016,22.988281 20.714844,8.0449219 Z M 20.232422,9.1015625 30.1875,23.332031 10.576172,30.240234 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,240 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="40"
height="40"
viewBox="0 0 40 40"
version="1.1"
id="svg17"
sodipodi:docname="toolbar_texture_displacement.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs17" />
<sodipodi:namedview
id="namedview17"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="32"
inkscape:cx="27.578125"
inkscape:cy="27.671875"
inkscape:window-width="3840"
inkscape:window-height="2126"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg17" />
<g
id="g17"
transform="translate(0.69176086,0.249987)">
<path
d="m 27.659236,35.176022 h -4.399987 c -0.508996,0 -0.844996,0 -0.954999,-0.394015 a 0.59299998,0.59299998 0 0 1 0.36,-0.669997 2.3,2.3 0 0 0 1.2,-2.188989 3.253,3.253 0 1 1 3.800012,3.205984 z M 24.12125,34.175997 h 2.988 a 2.253,2.253 0 1 0 -2.252999,-2.252976 3.445,3.445 0 0 1 -0.735001,2.252976 z"
style="fill:#009688;stroke-width:0.999999"
id="path1-6" />
<path
d="m 30.281245,30.538996 -0.846009,-0.534993 4.364031,-6.899981 A 0.222,0.222 0 0 0 33.439456,22.84588 l -5.4,6.644032 -0.776013,-0.63103 5.400001,-6.643994 a 1.222,1.222 0 0 1 1.981984,1.422992 z"
style="fill:#009688;stroke-width:0.999999"
id="path2-0" />
<path
d="m 33.281245,35.250026 h -0.999987 a 0.500013,0.500013 0 0 1 0,-1.000026 h 0.999987 a 0.49999998,0.49999998 0 0 0 0.499994,-0.499993 v -5.000013 a 0.5000126,0.5000126 0 0 1 1.000025,0 v 5.000013 a 1.5,1.5 0 0 1 -1.500019,1.500019 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path3-6" />
<path
d="M 19.281251,35.250026 H 5.28125 a 1.5,1.5 0 0 1 -1.5,-1.500019 V 5.750019 A 1.5,1.5 0 0 1 5.28125,4.25 h 27.999995 a 1.5,1.5 0 0 1 1.500019,1.500019 v 12.999987 a 0.5000126,0.5000126 0 0 1 -1.000025,0 V 5.750019 A 0.49999998,0.49999998 0 0 0 33.281245,5.250025 H 5.28125 A 0.49999998,0.49999998 0 0 0 4.781253,5.750019 V 33.750007 A 0.49999998,0.49999998 0 0 0 5.28125,34.25 h 14.000001 a 0.500013,0.500013 0 0 1 0,1.000026 z"
style="fill:#b6b6b6;stroke-width:0.999999"
id="path4-6" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-1"
width="39.379108"
height="0.99609375"
x="-19.909107"
y="27.094706"
ry="0.49804688"
transform="rotate(-45)" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2"
width="27.566595"
height="0.99609375"
x="7.9031763"
y="-0.27849278"
ry="0.49487707"
transform="rotate(45)" />
<g
id="g7"
transform="matrix(3.7795276,0,0,3.7795276,-350.69182,-427.25001)">
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2"
width="2.8453603"
height="0.2635498"
x="150.08304"
y="13.289762"
ry="0.1317749"
transform="rotate(45)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-2"
width="1.7498204"
height="0.2635498"
x="12.763216"
y="-149.44461"
ry="0.1317749"
transform="rotate(135)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-2-2"
width="0.82876241"
height="0.27337581"
x="13.654455"
y="-148.49495"
ry="0.1317749"
transform="rotate(135)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-6"
width="2.5927844"
height="0.25224927"
x="150.38367"
y="12.286384"
ry="0.12612464"
transform="rotate(45)"
rx="0.12612464" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-6-1"
width="1.9647908"
height="0.2635498"
x="151.01166"
y="11.21043"
ry="0.1317749"
transform="rotate(45)"
rx="0.1317749" />
</g>
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-8"
width="10.754118"
height="0.99609375"
x="-10.211728"
y="-31.661213"
ry="0.49804688"
transform="rotate(135)"
rx="0.49804688" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-2-9"
width="6.6134944"
height="0.99609375"
x="-33.794979"
y="12.679931"
ry="0.49804688"
transform="rotate(-135)"
rx="0.49804688" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-2-2-3"
width="3.1323304"
height="1.0332314"
x="-30.282875"
y="16.213961"
ry="0.49804688"
transform="rotate(-135)"
rx="0.49804688" />
<g
id="g7-1"
transform="matrix(0,-3.7795276,3.7795276,0,-427.55829,389.70233)">
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-9"
width="2.8453603"
height="0.2635498"
x="150.08304"
y="13.289762"
ry="0.1317749"
transform="rotate(45)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-2-8"
width="1.7498204"
height="0.2635498"
x="12.763216"
y="-149.44461"
ry="0.1317749"
transform="rotate(135)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-2-2-6"
width="0.82876241"
height="0.27337581"
x="13.654455"
y="-148.49495"
ry="0.1317749"
transform="rotate(135)"
rx="0.1317749" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-6-5"
width="2.5927844"
height="0.25224927"
x="150.38367"
y="12.286384"
ry="0.12612464"
transform="rotate(45)"
rx="0.12612464" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:0.264999;stroke-dasharray:none"
id="rect6-2-2-6-1-0"
width="1.9647908"
height="0.2635498"
x="151.01166"
y="11.21043"
ry="0.1317749"
transform="rotate(45)"
rx="0.1317749" />
</g>
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-9-4"
width="9.0822706"
height="0.99609375"
x="-36.379971"
y="-4.5927153"
ry="0.49804688"
transform="rotate(-135)"
rx="0.49804688" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-6-5-5"
width="9.7995005"
height="0.95338309"
x="-36.915619"
y="-8.385005"
ry="0.47669154"
transform="rotate(-135)"
rx="0.47669154" />
<rect
style="fill:#009688;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect6-2-2-6-1-0-0"
width="7.425981"
height="0.99609375"
x="-34.54211"
y="-12.451604"
ry="0.49804688"
transform="rotate(-135)"
rx="0.49804688" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -1,133 +0,0 @@
#version 110
// See resources/shaders/140/texture_displacement_bump.fs for full documentation; this is the
// GLSL 1.10 compatibility variant (same logic, older syntax).
#define INTENSITY_CORRECTION 0.6
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
uniform vec4 uniform_color;
uniform bool volume_mirrored;
uniform mat4 view_model_matrix;
uniform mat3 view_normal_matrix;
uniform sampler2D height_tex;
uniform vec2 height_tex_texel;
uniform float depth_mm;
uniform float tiling_scale;
uniform float rotation_rad;
uniform vec2 uv_offset;
uniform bool invert;
uniform bool use_vertex_uv;
// 2x3 affine (lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the dragged island's uv; see the
// 140 variant. Identity when nothing is dragged.
uniform vec4 island_delta_lin;
uniform vec2 island_delta_tr;
varying vec3 clipping_planes_dots;
varying vec4 model_pos;
varying vec4 world_pos;
varying float weight;
varying float island_active;
varying vec2 vertex_uv;
void projection_axes(vec3 n, out vec3 t, out vec3 b)
{
vec3 an = abs(n);
if (an.x >= an.y && an.x >= an.z) { // planar = p.yz
t = vec3(0.0, 1.0, 0.0);
b = vec3(0.0, 0.0, 1.0);
} else if (an.y >= an.x && an.y >= an.z) { // planar = p.xz
t = vec3(1.0, 0.0, 0.0);
b = vec3(0.0, 0.0, 1.0);
} else { // planar = p.xy
t = vec3(1.0, 0.0, 0.0);
b = vec3(0.0, 1.0, 0.0);
}
}
vec2 project_uv(vec3 p, vec3 n)
{
vec3 an = abs(n);
vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy);
planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset;
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
if (volume_mirrored)
triangle_normal = -triangle_normal;
if (use_vertex_uv) {
// Mikkelsen surface-gradient bump; see the 140 variant for the full rationale. Scale-exact
// for a conformal LSCM map (no global 1/tiling assumption), and gated by the paint weight
// via a multiply so the branch stays uniform (use_vertex_uv is a uniform).
vec2 uv = (island_active > 0.5)
? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr
: vertex_uv;
float h = texture2D(height_tex, uv).r;
float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0);
vec3 sigmaS = dFdx(model_pos.xyz);
vec3 sigmaT = dFdy(model_pos.xyz);
vec3 R1 = cross(sigmaT, triangle_normal);
vec3 R2 = cross(triangle_normal, sigmaS);
float det = dot(sigmaS, R1);
float dHdx = k * dFdx(h);
float dHdy = k * dFdy(h);
if (abs(det) > 1e-12)
triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det);
} else if (weight > 0.0) {
vec2 uv = project_uv(model_pos.xyz, triangle_normal);
vec3 t, b;
projection_axes(triangle_normal, t, b);
float hL = texture2D(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r;
float hR = texture2D(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r;
float hD = texture2D(height_tex, uv - vec2(0.0, height_tex_texel.y)).r;
float hU = texture2D(height_tex, uv + vec2(0.0, height_tex_texel.y)).r;
vec2 dh_duv = vec2((hR - hL) / (2.0 * height_tex_texel.x), (hU - hD) / (2.0 * height_tex_texel.y));
float inv_tiling = (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float amplitude = (invert ? -1.0 : 1.0) * depth_mm * inv_tiling * clamp(weight, 0.0, 1.0);
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs);
vec3 gradient = slope.x * t + slope.y * b;
gradient -= triangle_normal * dot(triangle_normal, gradient);
triangle_normal = normalize(triangle_normal - gradient);
}
vec3 eye_normal = normalize(view_normal_matrix * triangle_normal);
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
vec2 intensity = vec2(0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec3 position = (view_model_matrix * model_pos).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View File

@@ -1,34 +0,0 @@
#version 110
// See resources/shaders/140/texture_displacement_bump.vs for full documentation; this is the
// GLSL 1.10 compatibility variant.
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat4 volume_world_matrix;
uniform vec2 z_range;
uniform vec4 clipping_plane;
attribute vec3 v_position;
attribute vec3 v_normal; // .x = paint weight (0/1); .y = 1 for the dragged island's vertices
attribute vec2 v_tex_coord; // precomputed texture uv, used only when use_vertex_uv is set
varying vec3 clipping_planes_dots;
varying vec4 model_pos;
varying vec4 world_pos;
varying float weight;
varying float island_active;
varying vec2 vertex_uv;
void main()
{
model_pos = vec4(v_position, 1.0);
world_pos = volume_world_matrix * model_pos;
gl_Position = projection_matrix * view_model_matrix * model_pos;
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
weight = v_normal.x;
island_active = v_normal.y;
vertex_uv = v_tex_coord;
}

View File

@@ -1,70 +0,0 @@
#version 110
// See resources/shaders/140/texture_displacement_uvcheck.fs; GLSL 1.10 compatibility variant.
#define INTENSITY_CORRECTION 0.6
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
uniform mat3 view_normal_matrix;
uniform bool volume_mirrored;
uniform int mode;
uniform float checker_freq;
uniform float tiling_scale;
uniform float rotation_rad;
uniform vec2 uv_offset;
uniform bool use_vertex_uv;
varying vec3 clipping_planes_dots;
varying vec4 model_pos;
varying vec4 world_pos;
varying float distortion;
varying vec2 vertex_uv;
vec2 project_uv(vec3 p, vec3 n)
{
vec3 an = abs(n);
vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy);
planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset;
}
vec3 heatmap(float t)
{
t = clamp(t, 0.0, 1.0);
return clamp(vec3(1.5 - abs(4.0 * t - 3.0),
1.5 - abs(4.0 * t - 2.0),
1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0);
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
if (volume_mirrored)
triangle_normal = -triangle_normal;
vec3 base;
if (mode == 1) {
base = heatmap(distortion);
} else {
vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal);
vec2 c = floor(uv * checker_freq);
float check = mod(c.x + c.y, 2.0);
base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86);
}
vec3 eye_normal = normalize(view_normal_matrix * triangle_normal);
float intensity = INTENSITY_AMBIENT + max(dot(eye_normal, LIGHT_TOP_DIR), 0.0) * LIGHT_TOP_DIFFUSE
+ max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE;
gl_FragColor = vec4(base * intensity, 1.0);
}

View File

@@ -1,31 +0,0 @@
#version 110
// See resources/shaders/140/texture_displacement_uvcheck.vs; GLSL 1.10 compatibility variant.
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat4 volume_world_matrix;
uniform vec2 z_range;
uniform vec4 clipping_plane;
attribute vec3 v_position;
attribute vec3 v_normal; // .x = per-vertex uv distortion
attribute vec2 v_tex_coord; // precomputed texture uv, used only when use_vertex_uv is set
varying vec3 clipping_planes_dots;
varying vec4 model_pos;
varying vec4 world_pos;
varying float distortion;
varying vec2 vertex_uv;
void main()
{
model_pos = vec4(v_position, 1.0);
world_pos = volume_world_matrix * model_pos;
gl_Position = projection_matrix * view_model_matrix * model_pos;
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
distortion = v_normal.x;
vertex_uv = v_tex_coord;
}

View File

@@ -1,4 +1,7 @@
#version 140
// Multisample depth texture for the anti-aliased outline (see 3DScene.cpp render_with_outline).
// Optional on the GLSL 140 path: if unavailable, fallback to a non-multisample depth texture.
#extension GL_ARB_texture_multisample : enable
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
//BBS: add grey and orange
@@ -36,7 +39,14 @@ uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
// The outline is a per-fragment discard mask, which the framebuffer MSAA cannot smooth, so the
// silhouette is resolved per sample from a multisample copy of the outlined model's depth buffer.
#ifdef GL_ARB_texture_multisample
uniform sampler2DMS depth_tex;
uniform int msaa_samples; // samples in depth_tex, 1 when MSAA is off
#else
uniform sampler2D depth_tex;
#endif
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
@@ -99,45 +109,87 @@ float GetTolerance(float d, float k)
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
// Depth of sample s at integer pixel coord.
#ifdef GL_ARB_texture_multisample
float FetchDepth(ivec2 coord, int s)
{
// texelFetch has no wrap mode, so clamp to the edge texel (sampler2D used CLAMP_TO_EDGE).
ivec2 sz = textureSize(depth_tex);
return abs(texelFetch(depth_tex, clamp(coord, ivec2(0), sz - 1), s).r);
}
#else
float FetchDepth(ivec2 coord, int s)
{
return abs(texture(depth_tex, (vec2(coord) + 0.5) / screen_size).r);
}
#endif
float DetectSilho(ivec2 coord, ivec2 dir, int s)
{
// -------------------------------------------
// x0 ___ x1----o
// :\ :
// x0 ___ x1----o
// :\ :
// r0 : \ : r1
// : \ :
// : \ :
// o---x2 ___ x3
//
// r0 and r1 are the differences between actual
// and expected (as if x0..3 where on the same
// plane) depth values.
// -------------------------------------------
float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
float x0 = FetchDepth(coord + dir*-2, s);
float x1 = FetchDepth(coord + dir*-1, s);
float x2 = FetchDepth(coord, s);
float x3 = FetchDepth(coord + dir* 1, s);
float d0 = (x1-x0);
float d1 = (x2-x3);
float r0 = x1 + d0 - x2;
float r1 = x2 + d1 - x1;
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
float DetectSilho(ivec2 coord, int s)
{
return max(
DetectSilho(fragCoord, vec2(1,0)), // Horizontal
DetectSilho(fragCoord, vec2(0,1)) // Vertical
DetectSilho(coord, ivec2(1,0), s), // Horizontal
DetectSilho(coord, ivec2(0,1), s) // Vertical
);
}
// Full response of one sample. Reduce the max() per sample and average only afterwards:
// max(mean) <= mean(max), and averaging first hollows out diagonal and curved lines.
float DetectSilhoSample(ivec2 coord, int s)
{
float v = DetectSilho(coord, s);
// Makes silhouettes thicker.
for (int i = 1; i <= INFLATE; ++i)
{
v = max(v, DetectSilho(coord + ivec2(i, 0), s));
v = max(v, DetectSilho(coord + ivec2(0, i), s));
}
return v;
}
// Average the per-sample coverage into the sub-pixel anti-aliasing of the line.
float DetectSilho(vec2 fragCoord)
{
ivec2 coord = ivec2(fragCoord);
#ifdef GL_ARB_texture_multisample
int n = max(msaa_samples, 1);
#else
const int n = 1;
#endif
float acc = 0.0;
for (int s = 0; s < n; ++s)
acc += DetectSilhoSample(coord, s);
return acc / float(n);
}
// Returns a lighting multiplier in [1 - shadow_intensity, 1]: < 1 where the fragment is
// occluded from the light in the shadow map. 3x3 PCF softens the edges.
float shadow_shade()
@@ -224,14 +276,7 @@ void main()
//BBS: add outline_color
if (is_outline) {
color = vec4((vec3(intensity.y) + color.rgb * intensity.x) * shade, color.a);
vec2 fragCoord = gl_FragCoord.xy;
float s = DetectSilho(fragCoord);
// Makes silhouettes thicker.
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
float s = DetectSilho(gl_FragCoord.xy);
if (s < 0.01)
discard;
out_color = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a);

View File

@@ -1,4 +1,7 @@
#version 140
// Multisample depth texture for the anti-aliased outline (see 3DScene.cpp render_with_outline).
// Optional on the GLSL 140 path: if unavailable, fallback to a non-multisample depth texture.
#extension GL_ARB_texture_multisample : enable
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const vec3 LightRed = vec3(0.78, 0.0, 0.0);
@@ -51,7 +54,14 @@ uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
// The outline is a per-fragment discard mask, which the framebuffer MSAA cannot smooth, so the
// silhouette is resolved per sample from a multisample copy of the outlined model's depth buffer.
#ifdef GL_ARB_texture_multisample
uniform sampler2DMS depth_tex;
uniform int msaa_samples; // samples in depth_tex, 1 when MSAA is off
#else
uniform sampler2D depth_tex;
#endif
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
@@ -100,12 +110,27 @@ float GetTolerance(float d, float k)
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
// Depth of sample s at integer pixel coord.
#ifdef GL_ARB_texture_multisample
float FetchDepth(ivec2 coord, int s)
{
float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
// texelFetch has no wrap mode, so clamp to the edge texel (sampler2D used CLAMP_TO_EDGE).
ivec2 sz = textureSize(depth_tex);
return abs(texelFetch(depth_tex, clamp(coord, ivec2(0), sz - 1), s).r);
}
#else
float FetchDepth(ivec2 coord, int s)
{
return abs(texture(depth_tex, (vec2(coord) + 0.5) / screen_size).r);
}
#endif
float DetectSilho(ivec2 coord, ivec2 dir, int s)
{
float x0 = FetchDepth(coord + dir*-2, s);
float x1 = FetchDepth(coord + dir*-1, s);
float x2 = FetchDepth(coord, s);
float x3 = FetchDepth(coord + dir* 1, s);
float d0 = (x1-x0);
float d1 = (x2-x3);
@@ -116,17 +141,45 @@ float DetectSilho(vec2 fragCoord, vec2 dir)
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
float DetectSilho(ivec2 coord, int s)
{
return max(
DetectSilho(fragCoord, vec2(1,0)),
DetectSilho(fragCoord, vec2(0,1))
DetectSilho(coord, ivec2(1,0), s),
DetectSilho(coord, ivec2(0,1), s)
);
}
// Full response of one sample. Reduce the max() per sample and average only afterwards:
// max(mean) <= mean(max), and averaging first hollows out diagonal and curved lines.
float DetectSilhoSample(ivec2 coord, int s)
{
float v = DetectSilho(coord, s);
// Makes silhouettes thicker.
for (int i = 1; i <= INFLATE; ++i)
{
v = max(v, DetectSilho(coord + ivec2(i, 0), s));
v = max(v, DetectSilho(coord + ivec2(0, i), s));
}
return v;
}
// Average the per-sample coverage into the sub-pixel anti-aliasing of the line.
float DetectSilho(vec2 fragCoord)
{
ivec2 coord = ivec2(fragCoord);
#ifdef GL_ARB_texture_multisample
int n = max(msaa_samples, 1);
#else
const int n = 1;
#endif
float acc = 0.0;
for (int s = 0; s < n; ++s)
acc += DetectSilhoSample(coord, s);
return acc / float(n);
}
float compute_ssao_factor(vec3 normal, vec3 view_dir, vec3 eye_pos)
{
vec3 normal_dx = dFdx(normal);
@@ -270,13 +323,7 @@ void main()
if (is_outline) {
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * 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);
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
float s = DetectSilho(gl_FragCoord.xy);
if (s < 0.01)
discard;
out_color = vec4(mix(shaded_color.rgb, getBackfaceColor(shaded_color.rgb), s), shaded_color.a);

View File

@@ -1,186 +0,0 @@
#version 140
// Fast, geometry-free preview of texture displacement: perturbs the *shading* normal from the
// height texture's local gradient (a bump map), faded out by the per-vertex paint weight. The
// true, exact result is what "Bake" produces via libslic3r/TextureDisplacement.cpp on the CPU.
//
// The bake displaces each surface point along its normal by H = +/- depth_mm * (h(uv) - midlevel),
// with uv from the layer's projection. The perturbed normal is the analytic
//
// N' = normalize(N - (dH/da) * T - (dH/db) * B)
//
// over any orthonormal surface tangent pair (T, B), where the two slopes are real mm-per-mm
// derivatives. Two things have to be right for the preview's apparent depth to match the bake's:
// the tangent frame the gradient is expressed in, and the uv->mm scale that turns a texel
// difference into a slope. Getting the scale wrong is a uniform flattening (a raw texel difference
// is dh over one texel step, not over one mm); getting the frame wrong tilts the bump along the
// wrong axes.
//
// Two projection paths:
// * Triplanar (use_vertex_uv = 0): uv and the tangent axes are derived in-shader from the dominant
// normal axis, mirroring libslic3r's project_planar()/apply_uv_transform(), and the slope is
// formed analytically (there is a closed-form uv, so 1 uv unit is exactly tiling_scale mm).
// * Precomputed uv (use_vertex_uv = 1, used for LSCM): uv comes per-vertex from the CPU (the LSCM
// unwrap with island placement + tiling/rotation/offset already folded in), and the perturbed
// normal is built with Mikkelsen's method -- the surface gradient taken straight from the
// screen-space derivatives of the sampled height and position. This makes no uv->mm scale
// assumption, which matters because an LSCM map is conformal, not isometric: the local mm-per-uv
// varies across the chart, so a single global 1/tiling factor (what an earlier version used) got
// the apparent depth wrong. This path is also what makes the fast preview follow the UV editor:
// move an island and its uv -- hence its bump -- moves with it.
#define INTENSITY_CORRECTION 0.6
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
uniform vec4 uniform_color;
uniform bool volume_mirrored;
uniform mat4 view_model_matrix;
uniform mat3 view_normal_matrix;
uniform sampler2D height_tex;
uniform vec2 height_tex_texel; // (1/width, 1/height) of height_tex
uniform float depth_mm;
uniform float tiling_scale;
uniform float rotation_rad;
uniform vec2 uv_offset;
uniform bool invert;
uniform bool use_vertex_uv; // true: sample at vertex_uv with a derived tangent frame (LSCM)
// A 2x3 affine (columns packed as lin = (m00, m01, m10, m11), tr = (m02, m12)) applied to the uv of
// the island currently being dragged in the UV editor (island_active > 0.5). Identity when nothing is
// dragged, so this whole path is a no-op then. Lets a UV island drag move the bump on the model with
// only a uniform update
uniform vec4 island_delta_lin;
uniform vec2 island_delta_tr;
in vec3 clipping_planes_dots;
in vec4 model_pos;
in vec4 world_pos;
in float weight;
in float island_active;
in vec2 vertex_uv;
out vec4 out_color;
// The two model-space axes the triplanar planar coordinate is read off, per dominant normal
// component - same choice libslic3r's project_planar() makes, so planar.x runs along t, planar.y
// along b.
void projection_axes(vec3 n, out vec3 t, out vec3 b)
{
vec3 an = abs(n);
if (an.x >= an.y && an.x >= an.z) { // planar = p.yz
t = vec3(0.0, 1.0, 0.0);
b = vec3(0.0, 0.0, 1.0);
} else if (an.y >= an.x && an.y >= an.z) { // planar = p.xz
t = vec3(1.0, 0.0, 0.0);
b = vec3(0.0, 0.0, 1.0);
} else { // planar = p.xy
t = vec3(1.0, 0.0, 0.0);
b = vec3(0.0, 1.0, 0.0);
}
}
vec2 project_uv(vec3 p, vec3 n)
{
vec3 an = abs(n);
vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy);
planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset;
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
if (volume_mirrored)
triangle_normal = -triangle_normal;
if (use_vertex_uv) {
// Precomputed-uv (LSCM) path - Mikkelsen's surface-gradient bump ("Bump Mapping
// Unparametrized Surfaces on the GPU"). The perturbed normal is derived straight from the
// screen-space derivatives of the *sampled height* and the position, so it is scale-exact
// with no uv->mm assumption at all - which is the whole point here: an LSCM map is conformal,
// not isometric, so the local mm-per-uv varies across the chart and the earlier "one global
// 1/tiling factor" got the depth visibly wrong. dFdx(h) captures the true on-screen rate of
// change however the chart is stretched or however fine the tiling is.
//
// use_vertex_uv is a uniform, so this whole branch is uniform control flow and the texture
// derivatives are well defined; the paint weight gates the result by a plain multiply (k)
// rather than a per-fragment branch, keeping it that way.
// The dragged island's uv rides a uniform affine so its bump moves without a rebuild; every
// other vertex (island_active == 0) samples its baked uv unchanged.
vec2 uv = (island_active > 0.5)
? vec2(dot(island_delta_lin.xy, vertex_uv), dot(island_delta_lin.zw, vertex_uv)) + island_delta_tr
: vertex_uv;
float h = texture(height_tex, uv).r;
float k = (invert ? -1.0 : 1.0) * depth_mm * clamp(weight, 0.0, 1.0);
vec3 sigmaS = dFdx(model_pos.xyz);
vec3 sigmaT = dFdy(model_pos.xyz);
vec3 R1 = cross(sigmaT, triangle_normal);
vec3 R2 = cross(triangle_normal, sigmaS);
float det = dot(sigmaS, R1);
float dHdx = k * dFdx(h);
float dHdy = k * dFdy(h);
if (abs(det) > 1e-12)
triangle_normal = normalize(triangle_normal - (dHdx * R1 + dHdy * R2) / det);
} else if (weight > 0.0) {
// Triplanar path: uv and the tangent axes are reconstructed in-shader from the dominant
// normal component (see header). The gradient is expressed analytically because there is a
// closed-form uv here, unlike the LSCM case.
vec2 uv = project_uv(model_pos.xyz, triangle_normal);
vec3 t, b;
projection_axes(triangle_normal, t, b);
float hL = texture(height_tex, uv - vec2(height_tex_texel.x, 0.0)).r;
float hR = texture(height_tex, uv + vec2(height_tex_texel.x, 0.0)).r;
float hD = texture(height_tex, uv - vec2(0.0, height_tex_texel.y)).r;
float hU = texture(height_tex, uv + vec2(0.0, height_tex_texel.y)).r;
// Central difference, per uv unit (not per texel).
vec2 dh_duv = vec2((hR - hL) / (2.0 * height_tex_texel.x), (hU - hD) / (2.0 * height_tex_texel.y));
// uv -> mm is 1/tiling_scale for the triplanar projection, so this turns the uv-space
// gradient into a real surface slope.
float inv_tiling = (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float amplitude = (invert ? -1.0 : 1.0) * depth_mm * inv_tiling * clamp(weight, 0.0, 1.0);
// uv was rotated by project_uv() while t/b are the unrotated model axes, so rotate the
// gradient back into the axes' frame.
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
vec2 slope = amplitude * vec2(dh_duv.x * cs + dh_duv.y * sn, -dh_duv.x * sn + dh_duv.y * cs);
vec3 gradient = slope.x * t + slope.y * b;
gradient -= triangle_normal * dot(triangle_normal, gradient);
triangle_normal = normalize(triangle_normal - gradient);
}
vec3 eye_normal = normalize(view_normal_matrix * triangle_normal);
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
vec2 intensity = vec2(0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec3 position = (view_model_matrix * model_pos).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
out_color = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View File

@@ -1,44 +0,0 @@
#version 140
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat4 volume_world_matrix;
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
uniform vec2 z_range;
// Clipping plane - general orientation. Used by the SLA gizmo.
uniform vec4 clipping_plane;
in vec3 v_position;
// GLModel's P3N3T2 layout (position + normal + texcoord), reused so this mesh builds and renders
// like any other GLModel rather than needing a bespoke vertex buffer. The two spare channels carry
// what the bump preview actually needs per vertex:
// v_normal.x -- the active layer's paint weight, 0 (untouched) or 1 (painted).
// v_normal.y -- 1 for a vertex of the island currently being dragged in the UV editor, else 0.
// The fragment shader applies island_delta to those vertices' uv, so a UV drag is a
// single uniform update rather than a whole-mesh rebuild (like Adjust placement).
// v_tex_coord -- the precomputed texture uv for this vertex, valid only when use_vertex_uv is set
// (i.e. the LSCM projection, where uv can't be reconstructed in the shader). The
// triplanar path ignores it and projects in the fragment shader instead.
in vec3 v_normal;
in vec2 v_tex_coord;
out vec3 clipping_planes_dots;
out vec4 model_pos;
out vec4 world_pos;
out float weight;
out float island_active;
out vec2 vertex_uv;
void main()
{
model_pos = vec4(v_position, 1.0);
world_pos = volume_world_matrix * model_pos;
gl_Position = projection_matrix * view_model_matrix * model_pos;
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
weight = v_normal.x;
island_active = v_normal.y;
vertex_uv = v_tex_coord;
}

View File

@@ -1,83 +0,0 @@
#version 140
// UV-check overlay for the texture-displacement gizmo, drawn over the painted patch so the LSCM
// unwrap can be sanity-checked on the real 3D surface (mode set by the `mode` uniform):
// mode 0 - Checker: a procedural checkerboard sampled at the layer's uv. Even squares that stay
// square everywhere on the model mean the unwrap is low-distortion; squares that smear or
// shear reveal exactly where it stretches. Same uv the bake samples, so what you see is
// where the texture actually lands.
// mode 1 - Distortion heatmap: the per-vertex area-distortion carried in `distortion`, blue
// (compressed) -> green (ideal) -> red (stretched).
// Both are lit with the same cheap two-light diffuse the bump preview uses, so the surface still
// reads as 3D.
#define INTENSITY_CORRECTION 0.6
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
uniform mat3 view_normal_matrix;
uniform bool volume_mirrored;
uniform int mode; // 0 checker, 1 distortion
uniform float checker_freq; // checker squares per uv unit (one uv unit == one texture tile)
uniform float tiling_scale;
uniform float rotation_rad;
uniform vec2 uv_offset;
uniform bool use_vertex_uv;
in vec3 clipping_planes_dots;
in vec4 model_pos;
in vec4 world_pos;
in float distortion;
in vec2 vertex_uv;
out vec4 out_color;
vec2 project_uv(vec3 p, vec3 n)
{
vec3 an = abs(n);
vec2 planar = (an.x >= an.y && an.x >= an.z) ? p.yz : ((an.y >= an.x && an.y >= an.z) ? p.xz : p.xy);
planar *= (tiling_scale > 1e-6) ? (1.0 / tiling_scale) : 1.0;
float cs = cos(rotation_rad);
float sn = sin(rotation_rad);
return vec2(planar.x * cs - planar.y * sn, planar.x * sn + planar.y * cs) + uv_offset;
}
// Blue -> cyan -> green -> yellow -> red over t in [0,1].
vec3 heatmap(float t)
{
t = clamp(t, 0.0, 1.0);
return clamp(vec3(1.5 - abs(4.0 * t - 3.0),
1.5 - abs(4.0 * t - 2.0),
1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0);
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
if (volume_mirrored)
triangle_normal = -triangle_normal;
vec3 base;
if (mode == 1) {
base = heatmap(distortion);
} else {
vec2 uv = use_vertex_uv ? vertex_uv : project_uv(model_pos.xyz, triangle_normal);
vec2 c = floor(uv * checker_freq);
float check = mod(c.x + c.y, 2.0);
// Two distinct greys, plus a faint tint on one set so orientation is readable at a glance.
base = (check < 0.5) ? vec3(0.22, 0.23, 0.26) : vec3(0.82, 0.83, 0.86);
}
vec3 eye_normal = normalize(view_normal_matrix * triangle_normal);
float intensity = INTENSITY_AMBIENT + max(dot(eye_normal, LIGHT_TOP_DIR), 0.0) * LIGHT_TOP_DIFFUSE
+ max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0) * LIGHT_FRONT_DIFFUSE;
out_color = vec4(base * intensity, 1.0);
}

View File

@@ -1,36 +0,0 @@
#version 140
// Vertex stage for the UV-check overlay (checker / distortion heatmap) drawn over the painted patch
// by GLGizmoTextureDisplacement. Reuses GLModel's P3N3T2 layout so it needs no bespoke buffer:
// v_normal.x - per-vertex UV distortion (uv-area / surface-area ratio, remapped so 0.5 = ideal);
// only the distortion mode reads it.
// v_tex_coord - precomputed texture uv, valid only when use_vertex_uv is set (LSCM); the checker
// mode reconstructs uv in the fragment shader otherwise.
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat4 volume_world_matrix;
uniform vec2 z_range;
uniform vec4 clipping_plane;
in vec3 v_position;
in vec3 v_normal;
in vec2 v_tex_coord;
out vec3 clipping_planes_dots;
out vec4 model_pos;
out vec4 world_pos;
out float distortion;
out vec2 vertex_uv;
void main()
{
model_pos = vec4(v_position, 1.0);
world_pos = volume_world_matrix * model_pos;
gl_Position = projection_matrix * view_model_matrix * model_pos;
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
distortion = v_normal.x;
vertex_uv = v_tex_coord;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -48,6 +48,7 @@
<span id="statusText" class="status-text"></span>
</footer>
</main>
<script src="../js/plugin-config-ui.js"></script>
<script src="index.js"></script>
</body>
</html>

View File

@@ -9,6 +9,12 @@ let selectedCapabilityType = "";
let selectedHasPresetOverride = false;
let selectedReadOnly = false;
// Whether the frame already holds the selected capability's custom UI (payloads are gated by
// IsCurrentCapability and every selection change clears the view). Saving re-sends the whole
// capability_config payload, and rebuilding the frame from it would reload the plugin's page under
// the user's cursor, so a loaded frame gets the new values posted in instead.
let customFrameLoaded = false;
function SafeJsonParse(text) {
try {
return JSON.parse(text);
@@ -193,6 +199,7 @@ function ClearCapabilityConfigView() {
if (custom) {
custom.hidden = true;
custom.removeAttribute("srcdoc");
customFrameLoaded = false;
}
if (text)
text.value = "";
@@ -248,8 +255,14 @@ function ApplyCapabilityConfig(payload) {
if (html) {
if (custom) {
const context = OrcaConfigContext(payload, "preset");
custom.hidden = false;
custom.srcdoc = BuildCustomConfigDocument(html, config);
if (customFrameLoaded && custom.contentWindow) {
custom.contentWindow.postMessage({ __orca: "config", config: config, context: context }, "*");
} else {
customFrameLoaded = true;
custom.srcdoc = BuildCustomConfigDocument(html, config, context);
}
}
if (editor)
editor.hidden = true;
@@ -260,6 +273,7 @@ function ApplyCapabilityConfig(payload) {
if (custom) {
custom.hidden = true;
custom.removeAttribute("srcdoc");
customFrameLoaded = false;
}
if (editor)
editor.hidden = false;
@@ -354,39 +368,6 @@ function ApplyCapabilityConfigSaved(payload) {
SetConfigValidation("");
}
// The whole host surface a custom config UI gets: read the config, save one, drop the preset's
// override, and be told when either lands. The frame is sandboxed into an opaque origin, so this
// bridge is its only channel.
function BuildCustomConfigDocument(html, config) {
// Inlined into a <script>: a stored "</script>" would close the tag early, so escape "<" — the
// literal stays valid JSON.
const seed = JSON.stringify(config).replace(/</g, "\\u003c");
const bridge = `<script>
(function () {
var handlers = [];
var current = ${seed};
window.orca = {
getConfig: function () { return current; },
saveConfig: function (cfg) { parent.postMessage({ __orca: "save", config: cfg }, "*"); },
restoreDefaults: function () { parent.postMessage({ __orca: "restore" }, "*"); },
onConfig: function (cb) {
if (typeof cb !== "function") return;
handlers.push(cb);
try { cb(current); } catch (e) {}
}
};
window.addEventListener("message", function (event) {
if (!event.data || event.data.__orca !== "config") return;
current = event.data.config || {};
handlers.forEach(function (handler) {
try { handler(current); } catch (e) {}
});
});
})();
<\/script>`;
return bridge + html;
}
function OnCustomConfigMessage(event) {
const custom = document.getElementById("configCustom");
// Only the frame we created, and only while it is actually showing.
@@ -432,6 +413,7 @@ document.addEventListener("DOMContentLoaded", () => {
// OnCustomConfigMessage matches on the frame's contentWindow, not the origin ("null" when
// sandboxed), and ignores anything else.
window.addEventListener("message", OnCustomConfigMessage);
OrcaWatchThemeForFrame("configCustom");
SendMessage("request_capabilities");
});

View File

@@ -15,6 +15,7 @@
<script type="text/javascript" src="../../data/text.js"></script>
<script type="text/javascript" src="../js/globalapi.js"></script>
<script type="text/javascript" src="../js/common.js"></script>
<script src="../js/plugin-config-ui.js"></script>
<script src="./index.js"></script>
<script src="./plugin-sort.js"></script>
<script src="../js/fuzzy-search.js"></script>

View File

@@ -90,6 +90,7 @@ function OnInit() {
// OnCustomConfigMessage matches on the frame's contentWindow, not the origin ("null" when
// sandboxed), and ignores anything else.
window.addEventListener("message", OnCustomConfigMessage);
OrcaWatchThemeForFrame("configCustom");
document.addEventListener("click", (event) => {
if (!event.target.closest(".ctx"))
@@ -601,15 +602,17 @@ function SourceLabel(source) {
return "Mine";
case "subscribed":
return "Subscribed";
case "orphaned":
return "Orphaned";
default:
return "Local";
}
}
// Shared Local/Subscribed/Mine pill, used both after the row name and in the info panel.
// Shared source pill, used both after the row name and in the info panel.
function SourceBadge(source) {
const normalized = String(source || "").toLowerCase();
const variant = (normalized === "mine" || normalized === "subscribed") ? normalized : "local";
const variant = (normalized === "mine" || normalized === "subscribed" || normalized === "orphaned") ? normalized : "local";
const badge = document.createElement("span");
badge.className = `plugin-source-badge source-${variant}`;
badge.textContent = SourceLabel(source);
@@ -653,7 +656,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
const labelCell = document.createElement("span");
labelCell.className = "label-cell";
const hasCloudLink = plugin.source === "mine" || plugin.source === "subscribed";
const hasCloudLink = plugin.source === "mine" || plugin.source === "subscribed" || plugin.source === "orphaned";
const pluginLabelText = plugin.label || plugin.name || plugin.plugin_id || "";
const canExpand = capabilityCount > 0;
@@ -704,7 +707,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
function SourceCell(plugin) {
const cell = document.createElement("span");
const normalized = String(plugin.source || "").toLowerCase();
const variant = (normalized === "mine" || normalized === "subscribed") ? normalized : "local";
const variant = (normalized === "mine" || normalized === "subscribed" || normalized === "orphaned") ? normalized : "local";
cell.className = `source-cell source-${variant}`;
const sourceLabel = document.createElement("span");
@@ -1077,7 +1080,7 @@ function ApplyCapabilityConfig(payload) {
if (html) {
if (custom) {
custom.hidden = false;
custom.srcdoc = BuildCustomConfigDocument(html, config);
custom.srcdoc = BuildCustomConfigDocument(html, config, OrcaConfigContext(payload, "global"));
}
if (editor)
editor.hidden = true;
@@ -1178,39 +1181,6 @@ function ApplyCapabilityConfigSaved(payload) {
SetConfigValidation("");
}
// The whole host surface a custom config UI gets: read the config, save one, restore the plugin's
// defaults, and be told when either lands. The frame is sandboxed into an opaque origin, so this
// bridge is its only channel.
function BuildCustomConfigDocument(html, config) {
// Inlined into a <script>: a stored "</script>" would close the tag early, so escape "<" — the
// literal stays valid JSON.
const seed = JSON.stringify(config).replace(/</g, "\\u003c");
const bridge = `<script>
(function () {
var handlers = [];
var current = ${seed};
window.orca = {
getConfig: function () { return current; },
saveConfig: function (cfg) { parent.postMessage({ __orca: "save", config: cfg }, "*"); },
restoreDefaults: function () { parent.postMessage({ __orca: "restore" }, "*"); },
onConfig: function (cb) {
if (typeof cb !== "function") return;
handlers.push(cb);
try { cb(current); } catch (e) {}
}
};
window.addEventListener("message", function (event) {
if (!event.data || event.data.__orca !== "config") return;
current = event.data.config || {};
handlers.forEach(function (handler) {
try { handler(current); } catch (e) {}
});
});
})();
<\/script>`;
return bridge + html;
}
function OnCustomConfigMessage(event) {
const custom = document.getElementById("configCustom");
// Only the frame we created, and only while it is actually showing.
@@ -1265,7 +1235,7 @@ function RenderDescription(plugin) {
return;
}
const isCloud = plugin && (plugin.source === "mine" || plugin.source === "subscribed");
const isCloud = plugin && (plugin.source === "mine" || plugin.source === "subscribed" || plugin.source === "orphaned");
if (isCloud && String(plugin?.sharing_token || "")) {
node.appendChild(document.createTextNode("View on OrcaCloud "));
const link = document.createElement("a");
@@ -1402,6 +1372,13 @@ function RenderDetailSummary(container, plugin) {
message.textContent = errorText || StatusDescription(plugin);
container.appendChild(message);
if (plugin.orphaned === true) {
const warning = document.createElement("div");
warning.className = "detail-description detail-warning-text";
warning.textContent = "Orphaned: This plugin is no longer subscribed or available in OrcaCloud. The local copy remains installed and can still be used.";
container.appendChild(warning);
}
const updateStatus = GetUpdateStatus(plugin);
if (updateStatus === "update_available") {
const note = document.createElement("div");

View File

@@ -251,6 +251,11 @@ body.pane-resizing {
font-weight: 600;
}
.source-cell.source-orphaned {
color: var(--plugin-status-warn);
font-weight: 600;
}
.source-cell.source-local {
color: var(--plugin-source-neutral-text);
}
@@ -648,6 +653,10 @@ body.pane-resizing {
color: var(--plugin-status-danger);
}
.detail-warning-text {
color: var(--plugin-status-warn);
}
.detail-status-chip {
display: inline-flex;
align-items: center;
@@ -915,6 +924,11 @@ body.pane-resizing {
color: var(--plugin-source-subscribed-text);
}
.plugin-source-badge.source-orphaned {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
}
.plugin-cloud-link {
color: var(--plugin-link-text);
cursor: pointer;

View File

@@ -0,0 +1,105 @@
// The host surface a plugin capability's custom configuration UI gets, shared by the Plugins dialog
// and by a preset's plugin configuration dialog. Both sandbox the page into an opaque origin, so this
// bridge is its only channel — and both must offer plugin authors the same one.
// The host theme (WebViewHostDialog::host_theme_vars_css) is injected as a ":root{--orca-*}" rule in
// <style id="orca-host-theme-vars">, but only on the top-level page, so a sandboxed config UI never
// sees it unless we hand it over. Relay that CSS verbatim instead of re-deriving it: C++ stays the
// only place the contract is spelled out, and the host re-themes the style in place, so reading it
// always yields the live theme.
function OrcaThemeSnapshot() {
const style = document.getElementById("orca-host-theme-vars");
return {
theme: document.documentElement.getAttribute("data-orca-theme") === "dark" ? "dark" : "light",
css: style ? style.textContent : ""
};
}
// Inlined into a <script> or a JSON payload: a stored "</script>" would close the tag early, so
// escape "<" — the literal stays valid JSON.
function OrcaInlineJson(value) {
return JSON.stringify(value).replace(/</g, "\\u003c");
}
// What a custom UI can learn about the surface it is edited on, kept to what changes the page's own
// behavior: "Restore defaults" writes the plugin's get_default_config() in the Plugins dialog but
// drops the preset's override in a preset dialog, so a page needs the scope to label its own button.
function OrcaConfigContext(payload, scope) {
return {
scope: scope,
readOnly: payload ? payload.read_only === true : false,
hasPresetOverride: payload ? payload.has_preset_override === true : false
};
}
// The whole host surface: read the config, save one, restore defaults, follow the theme, and be told
// when any of it lands.
function BuildCustomConfigDocument(html, config, context) {
const theme = OrcaThemeSnapshot();
const bridge = `<style id="orca-host-theme-vars"></style>
<script>
(function () {
var handlers = [];
var themeHandlers = [];
var current = ${OrcaInlineJson(config === undefined ? {} : config)};
var context = ${OrcaInlineJson(context || {})};
var theme = ${OrcaInlineJson(theme)};
function applyTheme(next) {
if (next && typeof next.css === "string") theme = next;
var style = document.getElementById("orca-host-theme-vars");
if (style) style.textContent = theme.css;
if (document.documentElement) document.documentElement.setAttribute("data-orca-theme", theme.theme);
themeHandlers.forEach(function (handler) {
try { handler(theme.theme); } catch (e) {}
});
}
window.orca = {
getConfig: function () { return current; },
saveConfig: function (cfg) { parent.postMessage({ __orca: "save", config: cfg }, "*"); },
restoreDefaults: function () { parent.postMessage({ __orca: "restore" }, "*"); },
getContext: function () { return context; },
onConfig: function (cb) {
if (typeof cb !== "function") return;
handlers.push(cb);
try { cb(current); } catch (e) {}
},
onTheme: function (cb) {
if (typeof cb !== "function") return;
themeHandlers.push(cb);
try { cb(theme.theme); } catch (e) {}
}
};
window.addEventListener("message", function (event) {
if (!event.data) return;
if (event.data.__orca === "theme") { applyTheme(event.data.theme); return; }
if (event.data.__orca !== "config") return;
current = event.data.config || {};
if (event.data.context) context = event.data.context;
handlers.forEach(function (handler) {
try { handler(current); } catch (e) {}
});
});
applyTheme();
})();
<\/script>`;
return bridge + html;
}
// The host re-themes an open dialog in place (WebViewHostDialog::host_theme_apply_js), which a
// sandboxed child frame never sees. Relay it so a custom UI follows a light/dark switch live.
function OrcaWatchThemeForFrame(frameId) {
const relay = () => {
const frame = document.getElementById(frameId);
if (!frame || frame.hidden || !frame.contentWindow)
return;
frame.contentWindow.postMessage({ __orca: "theme", theme: OrcaThemeSnapshot() }, "*");
};
new MutationObserver(relay).observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-orca-theme"]
});
}

View File

@@ -3,18 +3,18 @@
# dependencies = ["numpy"]
#
# [tool.orcaslicer.plugin]
# name = "Orca Inspector Example"
# name = "Orca Inspector"
# description = "An interactive panel that browses the whole orca.host read-only API and demos every orca.host.ui facility."
# author = "OrcaSlicer"
# version = "0.0.1"
# author = "SoftFever"
# version = "0.0.3"
# ///
"""Orca Inspector — the worked example for `orca.host` and `orca.host.ui`.
"""Orca Inspector — a guided tour of `orca.host` and `orca.host.ui`.
Run it from the Plugins dialog. It opens a NON-MODAL window (OrcaSlicer stays
usable) with a sidebar of sections, each exercising one part of the API:
Overview plater state, scene statistics, current printer/process/filaments
Presets every PresetCollection, preset flags, any preset's full config
Presets every PresetCollection, filament slots, config viewer windows
Config the complete merged full_config as a searchable table
Model Model -> Object -> Volume/Instance tree with lazy mesh geometry
Assembly objects grouped by module_name, per-instance assemble transforms
@@ -32,9 +32,10 @@ and heavy mesh geometry (zero-copy numpy arrays, world-space math, the little
point-cloud previews) only when you ask for it. "Refresh" drops the cache and
refetches the visible section.
Style rules the page follows (see the plugin docs):
- no hardcoded colors only the host-injected --orca-* theme variables, so
the panel matches light and dark mode automatically;
Style rules the pages follow (see the plugin docs):
- no hardcoded theme: BASE_CSS aliases the host-injected --orca-* variables
(with fallbacks so the raw HTML previews in a plain browser) and styles the
body and native controls from them the host injects variables only;
- self-contained HTML (inline CSS/JS, no external resources);
- on_message runs on the UI thread, so long work (the progress demos) is
offloaded to a thread and results are pushed back with win.post().
@@ -45,6 +46,7 @@ transforms need bindings added in July 2026 — on older builds the panel shows
what is missing instead of failing.
"""
import json
import re
import threading
import time
@@ -125,6 +127,19 @@ def split_config_list(value):
# --------------------------------------------------------------------------- #
# section builders
# --------------------------------------------------------------------------- #
def filament_slots(bundle):
"""One entry per filament slot: the mounted preset (current_filament_presets)
plus its color and material from the merged config."""
cfg = bundle.full_config_value
colors = split_config_list(cfg("filament_colour"))
types = split_config_list(cfg("filament_type"))
return [{"slot": i + 1,
"color": colors[i] if i < len(colors) else "",
"material": types[i] if i < len(types) else "",
"preset": preset_dict(preset) if preset else None}
for i, preset in enumerate(bundle.current_filament_presets())]
def build_overview():
plater = orca.host.plater()
model = orca.host.model()
@@ -132,17 +147,7 @@ def build_overview():
objects = model.objects()
cfg = bundle.full_config_value
colors = split_config_list(cfg("filament_colour"))
types = split_config_list(cfg("filament_type"))
filaments = []
for i, preset in enumerate(bundle.current_filament_presets()):
filaments.append({
"name": preset.name if preset else "<missing preset>",
"color": colors[i] if i < len(colors) else "",
"type": types[i] if i < len(types) else "",
"is_system": bool(preset.is_system) if preset else False,
"is_dirty": bool(preset.is_dirty) if preset else False,
})
filaments = filament_slots(bundle)
printer = bundle.current_printer_preset()
process = bundle.current_process_preset()
@@ -189,13 +194,12 @@ def build_overview():
}
# label -> how to reach the PresetCollection on the bundle
# PresetCollection attribute -> label, in the order the Presets tab stacks its groups.
# The bundle also exposes sla_prints/sla_materials, omitted as OrcaSlicer has no SLA.
COLLECTIONS = [
("prints", "Process"),
("printers", "Printer"),
("filaments", "Filament"),
("sla_prints", "SLA print"),
("sla_materials", "SLA material"),
("prints", "Process"),
]
@@ -206,21 +210,25 @@ def build_presets():
coll = getattr(bundle, attr)
names = coll.preset_names()
selected = coll.selected_preset()
selected_name = coll.selected_preset_name()
shown = names[:MAX_PRESET_NAMES]
if selected_name and selected_name not in shown:
shown.insert(0, selected_name) # the active preset stays pickable past the cap
collections.append({
"key": attr,
"label": label,
"size": coll.size(),
"selected_name": coll.selected_preset_name(),
"selected_name": selected_name,
# selected = as saved on disk; edited = selected + unsaved modifications
"selected": {"name": selected.name, "is_dirty": selected.is_dirty,
"config_key_count": len(selected.config_keys())},
"edited": preset_dict(coll.edited_preset()),
"names": names[:MAX_PRESET_NAMES],
"names": shown,
"truncated": max(0, len(names) - MAX_PRESET_NAMES),
})
return {
"collections": collections,
"filament_names": bundle.current_filament_preset_names(),
"filament_slots": filament_slots(bundle),
}
@@ -231,8 +239,7 @@ def build_preset_config(collection_key, name):
preset = getattr(bundle, collection_key).find_preset(name)
if preset is None:
raise ValueError(f"preset {name!r} not found")
return {"preset": preset_dict(preset),
"rows": config_rows(preset.config_keys(), preset.config_value)}
return config_rows(preset.config_keys(), preset.config_value)
def build_config():
@@ -467,102 +474,149 @@ SECTION_BUILDERS = {
# --------------------------------------------------------------------------- #
# the page — sidebar app, themed exclusively via the injected --orca-* vars
# the pages — themed via BASE_CSS, which aliases the injected --orca-* vars
# --------------------------------------------------------------------------- #
# The host injects theme *variables* only (never element styles), so every page owns
# its base look. The fallbacks keep the raw HTML previewable in a plain browser.
BASE_CSS = r"""
:root {
--bg: var(--orca-bg, #ffffff);
--fg: var(--orca-fg, #1f2429);
--muted: var(--orca-muted, #6b7580);
--border: var(--orca-border, #d9dee3);
--accent: var(--orca-accent, #009688);
--accent-fg: var(--orca-accent-fg, #ffffff);
--ui: var(--orca-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: var(--orca-bg, #2b2d30);
--fg: var(--orca-fg, #e4e6e8);
--muted: var(--orca-muted, #9aa0a6);
--border: var(--orca-border, #3d4043);
}
}
* { box-sizing: border-box; }
body { margin:0; background:var(--bg); color:var(--fg); font:13px/1.45 var(--ui); }
button { font:inherit; padding:4px 12px; cursor:pointer; border-radius:6px;
background:var(--accent); color:var(--accent-fg); border:1px solid var(--accent); }
button.secondary { background:transparent; color:var(--fg); border-color:var(--border); }
button:disabled { opacity:.55; cursor:default; }
input, select { font:inherit; color:var(--fg); background:var(--bg);
border:1px solid var(--border); border-radius:6px; padding:4px 8px; }
:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
"""
# Identity hues for the preset collections, shared by the main page's group styling and
# the config viewer window. Deliberately the same in light and dark mode: they say what a
# card is, not what theme is on.
HUES = {"printers": "#9a6ee8", "filaments": "#ee8f41", "prints": "#4e8df6"}
HUE_CSS = "".join(f" .hue-{key} {{ --hue:{color}; }}\n" for key, color in HUES.items())
# The filterable key/value config table, shared by the Config tab and the viewer window.
CFG_TABLE_CSS = r"""
table.cfg { width:100%; border-collapse:collapse; font-size:12px; }
table.cfg th { text-align:left; font-size:11px; letter-spacing:.07em;
text-transform:uppercase; color:var(--muted); padding-bottom:4px; }
table.cfg td:first-child { font-family:var(--mono); white-space:nowrap;
vertical-align:top; }
table.cfg td.val { font-family:var(--mono); white-space:pre-wrap; word-break:break-word; }
td.val .more { color:var(--accent); cursor:pointer; }
"""
PAGE = r"""<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
:root { --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
* { box-sizing: border-box; }
body { margin:0; height:100vh; display:flex; flex-direction:column; overflow:hidden; }
<style>""" + BASE_CSS + r"""
body { height:100vh; display:flex; flex-direction:column; overflow:hidden; }
header { flex:none; display:flex; align-items:center; gap:10px;
padding:10px 16px; border-bottom:1px solid var(--orca-border); }
padding:10px 16px; border-bottom:1px solid var(--border); }
header h1 { font-size:15px; margin:0; }
#stamp { flex:1; color:var(--orca-muted); font-size:12px; }
button.secondary { background:transparent; color:var(--orca-fg);
border-color:var(--orca-border); }
#stamp { flex:1; color:var(--muted); font-size:12px; }
button.small { padding:2px 10px; font-size:12px; }
main { flex:1; display:flex; min-height:0; }
nav { flex:none; width:150px; padding:8px 0; overflow-y:auto;
border-right:1px solid var(--orca-border); }
border-right:1px solid var(--border); }
nav .item { padding:8px 14px; cursor:pointer; user-select:none;
color:var(--orca-muted); border-left:3px solid transparent; }
nav .item:hover { color:var(--orca-fg); }
nav .item.active { color:var(--orca-fg); font-weight:600;
border-left-color:var(--orca-accent); }
color:var(--muted); border-left:3px solid transparent; }
nav .item:hover { color:var(--fg); }
nav .item.active { color:var(--fg); font-weight:600;
border-left-color:var(--accent); }
nav .glyph { display:inline-block; width:1.5em; }
#content { flex:1; overflow:auto; padding:14px 16px 28px; }
.tiles { display:grid; grid-template-columns:repeat(auto-fill,minmax(108px,1fr));
gap:10px; margin-bottom:12px; }
.tile { border:1px solid var(--orca-border); border-radius:8px; padding:10px 12px; }
.tile { border:1px solid var(--border); border-radius:8px; padding:10px 12px; }
.tile .v { font-size:20px; font-weight:600; font-variant-numeric:tabular-nums; }
.tile .l { font-size:11px; color:var(--orca-muted); margin-top:2px; }
.tile .l { font-size:11px; color:var(--muted); margin-top:2px; }
.cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:12px; }
.card { border:1px solid var(--orca-border); border-radius:8px;
.card { border:1px solid var(--border); border-radius:8px;
padding:12px 14px; min-width:0; }
.card h3 { margin:0 0 8px; font-size:11px; letter-spacing:.07em;
text-transform:uppercase; color:var(--orca-muted); }
text-transform:uppercase; color:var(--muted); }
.card.wide { grid-column:1 / -1; }
""" + HUE_CSS + r""" .card.hued { border-top:3px solid var(--hue); }
.dot { display:inline-block; width:9px; height:9px; border-radius:50%;
background:var(--hue); margin-right:6px; }
.pgroup { margin-bottom:18px; }
.pgroup-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.pgroup-head h2 { margin:0; font-size:12px; letter-spacing:.07em; text-transform:uppercase; }
.pgroup-head select { flex:0 1 260px; min-width:120px; }
.spacer { flex:1; }
.kv { display:grid; grid-template-columns:minmax(110px,max-content) 1fr;
gap:3px 14px; font-size:12px; }
.kv .k { color:var(--orca-muted); }
.kv .k { color:var(--muted); }
.kv .v { font-family:var(--mono); word-break:break-word; }
.badge { display:inline-block; border:1px solid var(--orca-border);
color:var(--orca-muted); border-radius:999px; padding:1px 8px;
.badge { display:inline-block; border:1px solid var(--border);
color:var(--muted); border-radius:999px; padding:1px 8px;
font-size:11px; margin:1px 3px 1px 0; white-space:nowrap; }
.badge.on { background:var(--orca-accent); border-color:var(--orca-accent);
color:var(--orca-accent-fg); }
.badge.on { background:var(--accent); border-color:var(--accent);
color:var(--accent-fg); }
.swatch { display:inline-block; width:12px; height:12px; border-radius:3px;
border:1px solid var(--orca-border); vertical-align:-2px; margin-right:5px; }
border:1px solid var(--border); vertical-align:-2px; margin-right:5px; }
.toolbar { display:flex; gap:8px; align-items:center; margin-bottom:10px; }
.toolbar input { flex:1; max-width:340px; }
.count { color:var(--orca-muted); font-size:12px; }
table.cfg { width:100%; border-collapse:collapse; font-size:12px; }
table.cfg td:first-child { font-family:var(--mono); white-space:nowrap;
vertical-align:top; }
table.cfg td.val { font-family:var(--mono); white-space:pre-wrap; word-break:break-word; }
td.val .more { color:var(--orca-accent); cursor:pointer; }
.count { color:var(--muted); font-size:12px; }
""" + CFG_TABLE_CSS + r"""
.node { margin:1px 0; }
.nhead { display:flex; align-items:center; gap:7px; padding:4px 8px;
border-radius:6px; cursor:pointer; user-select:none; flex-wrap:wrap; }
.nhead:hover { background:var(--orca-border); }
.twist { width:1em; flex:none; color:var(--orca-muted); font-size:10px; }
.nhead:hover { background:var(--border); }
.twist { width:1em; flex:none; color:var(--muted); font-size:10px; }
.nname { font-weight:600; }
.nbody { display:none; margin-left:14px; padding:4px 0 4px 12px;
border-left:1px dotted var(--orca-border); }
border-left:1px dotted var(--border); }
.node.open > .nhead .twist { transform:rotate(90deg); }
.node.open > .nbody { display:block; }
.subhead { margin:8px 0 4px; font-size:11px; letter-spacing:.07em;
text-transform:uppercase; color:var(--orca-muted); }
text-transform:uppercase; color:var(--muted); }
.banner { border:1px solid var(--orca-border); border-left:3px solid var(--orca-accent);
.banner { border:1px solid var(--border); border-left:3px solid var(--accent);
border-radius:6px; padding:9px 12px; margin-bottom:12px; font-size:12px; }
.muted { color:var(--orca-muted); }
.muted { color:var(--muted); }
.mono { font-family:var(--mono); }
canvas.preview { width:100%; height:160px; border:1px solid var(--orca-border);
canvas.preview { width:100%; height:160px; border:1px solid var(--border);
border-radius:6px; }
.previews { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:8px; }
.previews .cap { font-size:11px; color:var(--orca-muted); text-align:center; margin-top:2px; }
.previews .cap { font-size:11px; color:var(--muted); text-align:center; margin-top:2px; }
.log { font-family:var(--mono); font-size:12px; border:1px solid var(--orca-border);
.log { font-family:var(--mono); font-size:12px; border:1px solid var(--border);
border-radius:6px; padding:8px 10px; max-height:190px; overflow:auto; }
.log div { padding:1px 0; }
fieldset { border:1px solid var(--orca-border); border-radius:6px; margin:0 0 10px; }
legend { color:var(--orca-muted); font-size:11px; padding:0 6px; }
fieldset { border:1px solid var(--border); border-radius:6px; margin:0 0 10px; }
legend { color:var(--muted); font-size:11px; padding:0 6px; }
.frow { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin:6px 0; }
label { font-size:12px; color:var(--orca-muted); }
label { font-size:12px; color:var(--muted); }
</style>
</head>
<body>
@@ -588,7 +642,7 @@ const SECTIONS = [
['assembly', '', 'Assembly'],
['uikit', '', 'UI Toolkit'],
];
const S = { current:'overview', cache:{}, busy:{}, colors:[], uiLog:[] };
const S = { current:'overview', cache:{}, busy:{}, colors:[], uiLog:[], gen:0 };
const $ = id => document.getElementById(id);
const esc = s => String(s == null ? '' : s)
@@ -598,6 +652,9 @@ const deg = r => num(r * 180 / Math.PI, 1) + '°';
const vec3 = (v, d=2) => v ? '(' + v.map(c => num(c, d)).join(', ') + ')' : '';
const yn = b => b ? 'yes' : 'no';
const badge = (label, on) => '<span class="badge' + (on ? ' on' : '') + '">' + esc(label) + '</span>';
// Colors land in a style attribute, so only literal hex colors pass through.
const swatch = c => /^#[0-9a-f]{3,8}$/i.test(c || '')
? '<span class="swatch" style="background:' + c + '"></span>' : '';
const kv = rows => '<div class="kv">' + rows.map(([k, v]) =>
'<div class="k">' + esc(k) + '</div><div class="v">' + v + '</div>').join('') + '</div>';
const bboxRows = (label, bb) => bb ? [
@@ -608,7 +665,8 @@ const bboxRows = (label, bb) => bb ? [
/* ------------------------------------------------------- nav + fetching */
function buildNav() {
$('nav').innerHTML = SECTIONS.map(([key, glyph, label]) =>
'<div class="item" id="nav-' + key + '" onclick="show(\'' + key + '\')">' +
'<div class="item" id="nav-' + key + '" role="button" tabindex="0" ' +
'onclick="show(\'' + key + '\')">' +
'<span class="glyph">' + glyph + '</span>' + label + '</div>').join('');
}
function show(key) {
@@ -622,9 +680,10 @@ function show(key) {
function fetchSection(key) {
S.busy[key] = true;
$('content').innerHTML = '<p class="muted">Loading ' + esc(key) + '…</p>';
orca.postMessage({ command:'fetch', section:key });
orca.postMessage({ command:'fetch', section:key, gen:S.gen });
}
function refresh() {
S.gen++; // replies to fetches from before this point are dropped
S.cache = {};
S.busy = {};
show(S.current);
@@ -660,13 +719,12 @@ function renderOverview(d) {
[t.config_keys, 'config keys'],
].map(([v, l]) => '<div class="tile"><div class="v">' + v + '</div><div class="l">' + l + '</div></div>').join('');
const filaments = d.setup.filaments.map((f, i) =>
'<div style="margin:3px 0">' +
(f.color ? '<span class="swatch" style="background:' + esc(f.color) + '"></span>' : '') +
'<b>' + (i + 1) + '</b> ' + esc(f.name) +
(f.type ? ' <span class="muted">' + esc(f.type) + '</span>' : '') +
badge(f.is_system ? 'system' : 'user', f.is_system) +
(f.is_dirty ? badge('modified', true) : '') +
const filaments = d.setup.filaments.map(f =>
'<div style="margin:3px 0">' + swatch(f.color) +
'<b>' + f.slot + '</b> ' + esc(f.preset ? f.preset.name : '<missing preset>') +
(f.material ? ' <span class="muted">' + esc(f.material) + '</span>' : '') +
(f.preset ? badge(f.preset.is_system ? 'system' : 'user', f.preset.is_system) +
(f.preset.is_dirty ? badge('modified', true) : '') : '') +
'</div>').join('') || '<span class="muted">none</span>';
return '<div class="tiles">' + tiles + '</div><div class="cards">' +
@@ -709,35 +767,65 @@ function presetBadges(p) {
(p.is_visible ? '' : badge('hidden', false));
}
function renderPresets(d) {
return '<div class="banner">Each card is one <span class="mono">host.PresetCollection</span>. ' +
'“Edited” is the selected preset plus any unsaved changes. Pick any preset ' +
'and load its complete config via <span class="mono">find_preset().config_value()</span>.</div>' +
'<div class="cards">' + d.collections.map(c => {
const p = c.edited;
const options = c.names.map(n => // explicit value= so odd whitespace in names survives
'<option value="' + esc(n) + '"' + (n === c.selected_name ? ' selected' : '') + '>' +
const detailCard = g => {
const p = g.edited;
return '<div class="cards"><div class="card hued">' +
'<div style="margin-bottom:6px"><b>' + esc(p.name) + '</b> ' + presetBadges(p) + '</div>' +
kv([['label()', esc(p.label)], ['alias', esc(p.alias) || ''],
['type', esc(p.type)], ['bundle', esc(p.bundle_id) || ''],
['edited_preset()', p.config_key_count + ' keys' +
(p.is_dirty ? ' · has unsaved changes' : ' · same as saved')],
['selected_preset()', esc(g.selected.name) + ' · ' + g.selected.config_key_count + ' keys'],
['file', '<span class="muted">' + esc(p.file) + '</span>']]) +
'</div></div>';
};
const slotCard = (s, g) => {
const p = s.preset;
if (!p) return '<div class="card hued"><h3>Slot ' + s.slot + '</h3>' +
'<p class="muted">no preset mounted</p></div>';
return '<div class="card hued"><h3>Slot ' + s.slot + '</h3>' +
'<div style="margin-bottom:6px">' + swatch(s.color) + '<b>' + esc(p.name) + '</b> ' +
badge(p.is_system ? 'system' : 'user', p.is_system) +
(p.is_dirty ? badge('modified', true) : '') +
(p.name === g.selected_name ? badge('editing', true) : '') + '</div>' +
kv([['material', esc(s.material) || ''],
['alias', esc(p.alias) || ''],
['config keys', p.config_key_count],
['file', '<span class="muted">' + esc(p.file) + '</span>']]) +
'<div class="frow" style="margin-top:8px"><button class="small secondary" ' +
'data-act="pcfg" data-coll="filaments" data-name="' + esc(p.name) + '">View config</button></div>' +
'</div>';
};
return '<div class="banner">Each group is one <span class="mono">host.PresetCollection</span>; ' +
'the Filament group shows every slot from <span class="mono">current_filament_presets()</span>, ' +
'and “editing” marks the preset open in its settings tab. Pick any preset in a group ' +
'header — “View config” opens its complete config (via ' +
'<span class="mono">find_preset().config_value()</span>) in its own window.</div>' +
d.collections.map(g => {
const options = g.names.map(n => // explicit value= so odd whitespace in names survives
'<option value="' + esc(n) + '"' + (n === g.selected_name ? ' selected' : '') + '>' +
esc(n) + '</option>').join('');
return '<div class="card"><h3>' + esc(c.label) + ' · ' + c.size + ' presets</h3>' +
'<div style="margin-bottom:6px"><b>' + esc(p.name) + '</b> ' + presetBadges(p) + '</div>' +
kv([['label()', esc(p.label)], ['alias', esc(p.alias) || ''],
['type', esc(p.type)], ['bundle', esc(p.bundle_id) || ''],
['edited_preset()', p.config_key_count + ' keys' +
(p.is_dirty ? ' · has unsaved changes' : ' · same as saved')],
['selected_preset()', esc(c.selected.name) + ' · ' + c.selected.config_key_count + ' keys'],
['file', '<span class="muted">' + esc(p.file) + '</span>']]) +
'<div class="frow" style="margin-top:8px">' +
'<select id="sel-' + c.key + '" style="flex:1;min-width:0">' + options + '</select>' +
'<button class="small" data-act="pcfg" data-coll="' + c.key + '">View config</button>' +
'</div>' +
(c.truncated ? '<p class="muted">list capped, ' + c.truncated + ' more not shown</p>' : '') +
'<div id="pcfg-' + c.key + '"></div></div>';
}).join('') + '</div>';
const cards = g.key === 'filaments'
? '<div class="cards">' + (d.filament_slots.map(s => slotCard(s, g)).join('') ||
'<p class="muted">no filament slots</p>') + '</div>'
: detailCard(g);
return '<div class="pgroup hue-' + g.key + '">' +
'<div class="pgroup-head"><span class="dot"></span><h2>' + esc(g.label) + '</h2>' +
'<span class="count">' + g.size + ' presets</span><span class="spacer"></span>' +
'<select id="sel-' + g.key + '">' + options + '</select>' +
'<button class="small" data-act="pcfg" data-coll="' + g.key + '">View config</button></div>' +
cards +
(g.truncated ? '<p class="muted">select capped, ' + g.truncated + ' more not listed</p>' : '') +
// the config itself opens in a viewer window; this slot only shows failures
'<div id="pcfg-' + g.key + '"></div></div>';
}).join('');
}
function configTable(rows, id) {
const body = rows.map(r => {
const long = r.v.length > 160;
const shown = long ? esc(r.v.slice(0, 160)) + '<span class="more" data-act="expand"> … show all</span>'
const shown = long ? esc(r.v.slice(0, 160)) +
'<span class="more" data-act="expand" role="button" tabindex="0"> … show all</span>'
: esc(r.v);
return '<tr data-k="' + esc(r.k.toLowerCase()) + '"><td>' + esc(r.k) + '</td>' +
'<td class="val" data-full="' + esc(r.v) + '">' + shown + '</td></tr>';
@@ -746,37 +834,36 @@ function configTable(rows, id) {
}
function renderConfig(d) {
return '<div class="toolbar"><input id="cfg-search" placeholder="Filter ' + d.count +
' keys…" oninput="filterConfig(this.value)">' +
' keys…" oninput="filterTable(\'cfg-table\', this.value, \'cfg-count\')">' +
'<span class="count" id="cfg-count">' + d.count + ' keys</span></div>' +
'<div class="banner">The merged result of printer + process + filament presets — ' +
'exactly what <span class="mono">preset_bundle().full_config_value(key)</span> returns for ' +
'every key in <span class="mono">full_config_keys()</span>.</div>' +
configTable(d.rows, 'cfg-table');
}
function filterConfig(q) {
function filterTable(tableId, q, countId) {
q = q.trim().toLowerCase();
let visible = 0;
document.querySelectorAll('#cfg-table tr[data-k]').forEach(tr => {
document.querySelectorAll('#' + tableId + ' tr[data-k]').forEach(tr => {
const hit = !q || tr.dataset.k.includes(q) ||
tr.querySelector('.val').dataset.full.toLowerCase().includes(q);
tr.style.display = hit ? '' : 'none';
if (hit) visible++;
});
$('cfg-count').textContent = visible + ' keys';
if (countId) $(countId).textContent = visible + ' keys';
}
function extruderChip(id) {
if (id == null || id < 1) return '<span class="badge">extruder —</span>';
const color = S.colors[id - 1];
return '<span class="badge">' + (color ? '<span class="swatch" style="background:' +
esc(color) + '"></span>' : '') + 'extruder ' + id + '</span>';
return '<span class="badge">' + swatch(S.colors[id - 1]) + 'extruder ' + id + '</span>';
}
const VOL_GLYPH = { ModelPart:'', NegativeVolume:'', ParameterModifier:'',
SupportEnforcer:'', SupportBlocker:'' };
function node(head, body, open) {
return '<div class="node' + (open ? ' open' : '') + '">' +
'<div class="nhead" data-act="toggle"><span class="twist">▶</span>' + head + '</div>' +
'<div class="nhead" data-act="toggle" role="button" tabindex="0" aria-expanded="' +
!!open + '"><span class="twist">▶</span>' + head + '</div>' +
'<div class="nbody">' + body + '</div></div>';
}
function paintedBadges(p) {
@@ -969,7 +1056,7 @@ function drawPoints(canvas, pts, ax, ay) {
PREVIEWS[canvas.id] = { pts, ax, ay };
const scaleDpr = window.devicePixelRatio || 1;
const w = canvas.clientWidth * scaleDpr, h = canvas.clientHeight * scaleDpr;
if (!w || !h) return; // inside a collapsed node; repainted on toggle
if (!w || !h) { delete canvas.dataset.painted; return; } // collapsed; repainted on toggle
canvas.dataset.painted = '1';
canvas.width = w; canvas.height = h;
const ctx = canvas.getContext('2d');
@@ -983,7 +1070,7 @@ function drawPoints(canvas, pts, ax, ay) {
(h - 2 * pad) / Math.max(maxY - minY, 1e-6));
const ox = (w - (maxX - minX) * scale) / 2, oy = (h - (maxY - minY) * scale) / 2;
const style = getComputedStyle(document.body);
ctx.strokeStyle = style.getPropertyValue('--orca-accent');
ctx.strokeStyle = style.getPropertyValue('--accent');
ctx.strokeRect(ox, oy, (maxX - minX) * scale, (maxY - minY) * scale);
ctx.fillStyle = style.color;
ctx.globalAlpha = 0.55;
@@ -992,6 +1079,21 @@ function drawPoints(canvas, pts, ax, ay) {
ctx.fillRect(ox + (p[ax] - minX) * scale - r / 2,
h - oy - (p[ay] - minY) * scale - r / 2, r, r);
}
// A live theme switch re-stamps data-orca-theme and a resize leaves the bitmap at its
// old size both need every known preview repainted.
function repaintPreviews() {
document.querySelectorAll('canvas.preview').forEach(c => {
const p = PREVIEWS[c.id];
if (p) drawPoints(c, p.pts, p.ax, p.ay);
});
}
new MutationObserver(repaintPreviews)
.observe(document.documentElement, { attributes:true, attributeFilter:['data-orca-theme'] });
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(repaintPreviews, 150);
});
/* ------------------------------------------------------------ UI toolkit */
function renderUikit() {
@@ -1052,6 +1154,7 @@ $('content').addEventListener('click', e => {
if (act === 'toggle') {
const node = t.parentElement;
node.classList.toggle('open');
t.setAttribute('aria-expanded', node.classList.contains('open'));
// Repaint any preview whose canvas was hidden (zero-size) when its data arrived.
node.querySelectorAll('canvas.preview').forEach(c => {
const p = PREVIEWS[c.id];
@@ -1062,9 +1165,10 @@ $('content').addEventListener('click', e => {
t.textContent = 'Loading…';
orca.postMessage({ command:'mesh', object:+t.dataset.o, volume:+t.dataset.v });
} else if (act === 'pcfg') {
const coll = t.dataset.coll;
const coll = t.dataset.coll; // slot buttons carry their preset in data-name
orca.postMessage({ command:'preset_config', collection:coll,
name:$('sel-' + coll).value });
name:t.dataset.name !== undefined ? t.dataset.name
: $('sel-' + coll).value });
} else if (act === 'expand') {
const td = t.closest('td');
td.textContent = td.dataset.full;
@@ -1072,11 +1176,22 @@ $('content').addEventListener('click', e => {
uiAction(t.dataset.ui);
}
});
// Keyboard parity for the click-only elements (nav items, tree toggles, "show all").
// Real form controls are skipped so they keep their native keys.
document.addEventListener('keydown', e => {
if (e.key !== 'Enter' && e.key !== ' ') return;
if (/^(BUTTON|INPUT|SELECT|TEXTAREA)$/.test(e.target.tagName)) return;
const t = e.target.closest('.item, [data-act]');
if (!t) return;
e.preventDefault();
t.click();
});
/* ------------------------------------------------------------- messages */
orca.onMessage(msg => {
if (!msg || !msg.command) return;
if (msg.command === 'section') {
if (msg.gen !== S.gen) return; // raced a Refresh; the refetch is in flight
S.cache[msg.section] = msg;
S.busy[msg.section] = false;
stamp();
@@ -1085,14 +1200,15 @@ orca.onMessage(msg => {
const container = $('mesh-' + msg.object + '-' + msg.volume);
if (!container) return;
if (msg.ok) renderMeshDetail(container, msg.data);
else container.innerHTML = '<span class="muted">⚠ ' + esc(msg.error) + '</span>';
else container.innerHTML =
'<button class="small secondary" data-act="mesh" data-o="' + msg.object +
'" data-v="' + msg.volume + '">Retry</button> <span class="muted">⚠ ' +
esc(msg.error) + '</span>';
} else if (msg.command === 'preset_config') {
// Success opened a viewer window; this slot only shows (or clears) failures.
const container = $('pcfg-' + msg.collection);
if (!container) return;
container.innerHTML = msg.ok
? '<div class="subhead">' + esc(msg.name) + ' · ' + msg.data.rows.length +
' keys</div>' + configTable(msg.data.rows, 'pcfg-table-' + msg.collection)
: '<span class="muted">⚠ ' + esc(msg.error) + '</span>';
container.innerHTML = msg.ok ? '' : '<p class="muted">⚠ ' + esc(msg.error) + '</p>';
} else if (msg.command === 'ui_result') {
addLog(msg.ok ? '' + msg.action + ': ' + msg.result
: '' + msg.action + ' failed: ' + msg.error);
@@ -1110,28 +1226,33 @@ show('overview');
# The modal page demoed from the UI Toolkit tab: create_window(style=WINDOW_MODAL)
# keeps the handle alive for callbacks; orca.submit(payload) invokes on_submit.
MODAL_PAGE = r"""<!DOCTYPE html>
<html><head><meta charset="utf-8"></head>
<body style="padding:18px">
<h3 style="margin-top:0">Modal dialog</h3>
<html lang="en"><head><meta charset="utf-8"><style>""" + BASE_CSS + r"""
body { padding:18px; }
h3 { margin-top:0; }
input { width:100%; }
</style></head>
<body>
<h3>Modal dialog</h3>
<p>create_window(style=WINDOW_MODAL) keeps this page interactive until it submits or closes.</p>
<p><input id="note" style="width:100%" value="hello from the modal"></p>
<p><input id="note" value="hello from the modal"></p>
<p style="text-align:right">
<button style="background:transparent;color:var(--orca-fg);border-color:var(--orca-border)"
onclick="orca.postMessage({live: document.getElementById('note').value})">Post while open</button>
<button style="background:transparent;color:var(--orca-fg);border-color:var(--orca-border)"
onclick="orca.close()">Cancel</button>
<button class="secondary" onclick="orca.postMessage({live: document.getElementById('note').value})">Post while open</button>
<button class="secondary" onclick="orca.close()">Cancel</button>
<button onclick="orca.submit({note: document.getElementById('note').value})">Submit</button>
</p>
</body></html>
"""
CHILD_PAGE = r"""<!DOCTYPE html>
<html><head><meta charset="utf-8"></head>
<body style="padding:18px">
<h3 style="margin-top:0">Child window</h3>
<html lang="en"><head><meta charset="utf-8"><style>""" + BASE_CSS + r"""
body { padding:18px; }
h3 { margin-top:0; }
</style></head>
<body>
<h3>Child window</h3>
<p>Same create_window() API one plugin, several windows.</p>
<p><button onclick="orca.postMessage({command:'ping'})">Ping the plugin</button></p>
<div id="log" style="color:var(--orca-muted)"></div>
<div id="log" style="color:var(--muted)"></div>
<script>
var n = 0;
orca.onMessage(function (msg) {
@@ -1142,6 +1263,75 @@ CHILD_PAGE = r"""<!DOCTYPE html>
</body></html>
"""
# The per-preset config viewer, opened by "View config" on the Presets tab. Its rows are
# baked in as JSON at build time, so the window needs no bridge traffic and stays usable
# side by side with the panel until closed.
CONFIG_PAGE = r"""<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><style>""" + BASE_CSS + CFG_TABLE_CSS + r"""
body { padding:12px 16px 20px; border-top:3px solid var(--hue, var(--accent)); }
.toolbar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px; }
.toolbar h3 { margin:0; font-size:13px; }
.toolbar input { flex:1; min-width:140px; }
.dot { display:inline-block; width:9px; height:9px; border-radius:50%; flex:none;
background:var(--hue, var(--accent)); }
.count { color:var(--muted); font-size:12px; white-space:nowrap; }
</style></head>
<body>
<div class="toolbar"><span class="dot"></span><h3 id="name"></h3>
<span class="count" id="count"></span>
<input id="q" placeholder="Filter keys…">
<button class="secondary" onclick="orca.close()">Close</button></div>
<div id="table"></div>
<script>
'use strict';
const DATA = __DATA__;
const $ = id => document.getElementById(id);
const esc = s => String(s == null ? '' : s)
.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
if (DATA.hue) document.body.style.setProperty('--hue', DATA.hue);
$('name').textContent = DATA.name;
$('table').innerHTML = '<table class="cfg" id="t"><tr><th>key</th><th>value</th></tr>' +
DATA.rows.map(r => {
const long = r.v.length > 160;
const shown = long ? esc(r.v.slice(0, 160)) +
'<span class="more" role="button" tabindex="0"> … show all</span>' : esc(r.v);
return '<tr data-k="' + esc(r.k.toLowerCase()) + '"><td>' + esc(r.k) + '</td>' +
'<td class="val" data-full="' + esc(r.v) + '">' + shown + '</td></tr>';
}).join('') + '</table>';
function filter(q) {
q = q.trim().toLowerCase();
let visible = 0;
document.querySelectorAll('#t tr[data-k]').forEach(tr => {
const hit = !q || tr.dataset.k.includes(q) ||
tr.querySelector('.val').dataset.full.toLowerCase().includes(q);
tr.style.display = hit ? '' : 'none';
if (hit) visible++;
});
$('count').textContent = visible + ' keys';
}
$('q').addEventListener('input', e => filter(e.target.value));
$('table').addEventListener('click', e => {
const t = e.target.closest('.more');
if (t) { const td = t.closest('td'); td.textContent = td.dataset.full; }
});
document.addEventListener('keydown', e => {
if ((e.key === 'Enter' || e.key === ' ') && e.target.classList.contains('more')) {
e.preventDefault();
e.target.click();
}
});
filter('');
</script>
</body></html>
"""
def config_page(collection, name, rows):
"""CONFIG_PAGE with one preset's rows baked in. `</` is escaped so a config
value containing e.g. `</script>` cannot break out of the script block."""
payload = json.dumps({"name": name, "hue": HUES.get(collection, ""), "rows": rows})
return CONFIG_PAGE.replace("__DATA__", payload.replace("</", "<\\/"))
# --------------------------------------------------------------------------- #
# the plugin
@@ -1150,6 +1340,7 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
win = None
child = None
modal = None
cfgwin = None
def get_name(self):
return "Orca Inspector"
@@ -1166,6 +1357,9 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
if self.modal is not None:
self.modal.close()
self.modal = None
if self.cfgwin is not None:
self.cfgwin.close()
self.cfgwin = None
# Non-modal: returns immediately. The window is host-owned and lives on
# after execute() returns; on_message keeps firing when the page posts.
self.win = orca.host.ui.create_window(
@@ -1184,11 +1378,11 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
msg = msg or {}
command = msg.get("command")
if command == "fetch":
self.send_section(msg.get("section", ""))
self.send_section(msg.get("section", ""), msg.get("gen"))
elif command == "mesh":
self.send_mesh(int(msg.get("object", -1)), int(msg.get("volume", -1)))
self.send_mesh(msg.get("object", -1), msg.get("volume", -1))
elif command == "preset_config":
self.send_preset_config(msg.get("collection", ""), msg.get("name", ""))
self.open_preset_config(msg.get("collection", ""), msg.get("name", ""))
elif command == "ui":
self.run_ui_action(msg)
@@ -1196,11 +1390,15 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
if self.child is not None:
self.child.close()
self.child = None
if self.cfgwin is not None:
self.cfgwin.close()
self.cfgwin = None
print("Orca Inspector closed")
def send_section(self, section):
def send_section(self, section, gen=None):
builder = SECTION_BUILDERS.get(section)
reply = {"command": "section", "section": section}
# gen echoes the page's refresh counter so a reply that raced a Refresh is dropped there.
reply = {"command": "section", "section": section, "gen": gen}
if builder is None:
self.win.post({**reply, "ok": False, "error": f"unknown section {section!r}"})
return
@@ -1213,15 +1411,23 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
reply = {"command": "mesh", "object": object_index, "volume": volume_index}
try:
self.win.post({**reply, "ok": True,
"data": build_mesh(object_index, volume_index)})
"data": build_mesh(int(object_index), int(volume_index))})
except Exception as exc:
self.win.post({**reply, "ok": False, "error": str(exc)})
def send_preset_config(self, collection, name):
def open_preset_config(self, collection, name):
# One viewer at a time: a new pick replaces the previous window. The main page
# only hears back about failures.
reply = {"command": "preset_config", "collection": collection, "name": name}
try:
self.win.post({**reply, "ok": True,
"data": build_preset_config(collection, name)})
rows = build_preset_config(collection, name)
if self.cfgwin is not None:
self.cfgwin.close()
self.cfgwin = orca.host.ui.create_window(
title=f"Orca Inspector — {name}",
html=config_page(collection, name, rows),
width=520, height=640)
self.win.post({**reply, "ok": True})
except Exception as exc:
self.win.post({**reply, "ok": False, "error": str(exc)})
@@ -1267,15 +1473,19 @@ class OrcaInspectorPanel(orca.script.ScriptPluginCapabilityBase):
if self.child is not None and self.child.is_open():
report("child already open")
else:
# Not `reply`: the close can also come from "child_close" later, so the
# on_close payload is labelled neutrally.
self.child = orca.host.ui.create_window(
title="Orca Inspector — child", html=CHILD_PAGE,
width=380, height=240, on_message=self.on_child_message,
on_close=lambda: self.win.post(
{**reply, "ok": True, "result": "child window closed"}))
{"command": "ui_result", "action": "child",
"ok": True, "result": "child window closed"}))
report("child opened")
elif action == "child_close":
if self.child is not None:
self.child.close()
report("close requested") # the actual close is logged by on_close
else:
report("no child window")
elif action == "child_state":

View File

@@ -0,0 +1,618 @@
# /// script
# requires-python = ">=3.12"
#
# [tool.orcaslicer.plugin]
# name = "Twistify"
# description = "Twists, tapers, and wobbles every layer's slice polygons as a function of Z (demo)."
# author = "SoftFever"
# version = "0.03"
# type = "slicing-pipeline"
# ///
"""Twistify -- twist/taper/wobble any model at slice time.
At Step.posSlice, every layer's sliced surfaces are transformed by a similarity
about the object's bounding-box center as a function of Z -- edited IN PLACE
through the host geometry classes (ExPolygon.rotate/scale/translate). Each
surface is rotated about the center, then (if tapering) translated to the
origin, uniformly scaled, and translated back, so the taper stays centered on
the object instead of drifting toward the coordinate origin. An optional X
wobble is applied last. After the per-region edits, layer.make_slices()
re-derives the layer's merged islands so overhang/bridge/skirt/support stay
coherent. The split slice loop runs make_perimeters() right after the hook, so
the transform cascades into perimeters, infill, and the final G-code -- the
preview corkscrews and the print keeps correct walls/infill/flow.
Because we edit geometry in place, surface types are preserved automatically
(no per-surface type carry needed), and no numpy is required --
rotate/scale/translate are host methods. Parameters come from self.get_config()
(see _params below); the host seeds them from get_default_config() the first
time the plugin loads. The first object layer is untouched (z_rel = 0), so bed
adhesion is unaffected.
The plugin also ships its own configuration UI (has_config_ui/get_config_ui):
a self-contained page the Plugins dialog renders instead of the JSON editor,
drawing the transform it is about to apply as an isometric stack of layer
outlines. It reaches the host only through the injected window.orca bridge --
getConfig/saveConfig/restoreDefaults/getContext/onConfig/onTheme -- and styles
itself from the --orca-* theme variables the host hands the frame, so it
follows OrcaSlicer's light/dark theme.
"""
import math
import json
import orca
_DEFAULTS = {
"twist_deg_per_mm": 1.0,
"taper_per_mm": 0.0,
"wobble_ampl_mm": 0.0,
"wobble_period_mm": 20.0,
"min_scale": 0.05,
}
def _params(self):
try:
src = json.loads(self.get_config())
except (AttributeError, TypeError, ValueError):
src = {}
out = {}
for key, default in _DEFAULTS.items():
try:
out[key] = float(src[key])
except (KeyError, TypeError, ValueError):
out[key] = default
return out
def _is_identity(p):
return p["twist_deg_per_mm"] == 0.0 and p["taper_per_mm"] == 0.0 and p["wobble_ampl_mm"] == 0.0
def _layer_params(z_rel, mm_to_scaled, p):
"""(angle_rad, scale, x_offset_scaled) for one layer. Exact identity at z_rel == 0."""
theta = math.radians(p["twist_deg_per_mm"] * z_rel)
s = max(p["min_scale"], 1.0 + p["taper_per_mm"] * z_rel)
ox = 0.0
if p["wobble_ampl_mm"] != 0.0 and p["wobble_period_mm"] > 0.0:
ox = p["wobble_ampl_mm"] * math.sin(2.0 * math.pi * z_rel / p["wobble_period_mm"]) * mm_to_scaled
return theta, s, ox
# The configuration page. Self-contained on purpose: it runs in an iframe sandboxed into an opaque
# origin, so it has only the window.orca bridge and the --orca-* theme variables the host injects --
# no network, no same-origin access, no shared stylesheet. get_config_ui() substitutes _DEFAULTS for
# __ORCA_DEFAULTS__, so Python owns the values and the page adds only presentation.
_CONFIG_UI = """
<style>
:root {
--ink: var(--orca-fg, #1f2429);
--paper: var(--orca-bg, #ffffff);
--rule: var(--orca-border, #d9dee3);
--quiet: var(--orca-muted, #6b7580);
--live: var(--orca-accent, #009688);
--live-ink: var(--orca-accent-fg, #ffffff);
--ui: var(--orca-font, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
--data: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
:root {
--ink: var(--orca-fg, #e4e6e8);
--paper: var(--orca-bg, #2b2d30);
--rule: var(--orca-border, #3d4043);
--quiet: var(--orca-muted, #9aa0a6);
}
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font: 13px/1.45 var(--ui);
}
.panel { display: flex; flex-direction: column; height: 100%; }
.head {
display: flex; align-items: baseline; gap: 10px;
padding: 12px 16px 10px;
border-bottom: 1px solid var(--rule);
}
.mark {
font: 10px/1 var(--data); letter-spacing: .14em; text-transform: uppercase;
color: var(--live);
}
.head-note { font-size: 11px; color: var(--quiet); }
/* The frame is as narrow as ~400px in a resized dialog and as wide as ~600px; below the
breakpoint the drawing moves under the controls rather than squeezing both. */
.work {
flex: 1; min-height: 0; overflow: auto;
display: grid; grid-template-columns: minmax(0, 1fr) 180px; gap: 16px;
align-content: start; padding: 14px 16px;
}
@media (max-width: 470px) {
.work { grid-template-columns: minmax(0, 1fr); }
.plot { max-width: 210px; }
}
.rows { display: flex; flex-direction: column; gap: 12px; }
.row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 4px 8px; }
.row-label {
grid-column: 1;
font: 10px/1 var(--data); letter-spacing: .12em; text-transform: uppercase;
color: var(--quiet);
}
.row-figure { grid-column: 2; display: flex; align-items: baseline; gap: 4px; }
.row-figure input {
/* No spinners: the rail is the coarse control, this is the exact one, and the arrows would
steal the width the value needs. */
-webkit-appearance: textfield; appearance: textfield;
width: 76px; padding: 3px 6px; text-align: right;
font: 13px/1.2 var(--data); font-variant-numeric: tabular-nums;
color: var(--ink); background: transparent;
border: 1px solid transparent; border-radius: 3px;
}
.row-figure input::-webkit-outer-spin-button,
.row-figure input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.row-figure input:hover { border-color: var(--rule); }
.row-figure input:focus { outline: none; border-color: var(--live); }
.unit { font: 10px/1 var(--data); color: var(--quiet); min-width: 26px; }
/* The rail carries a detent at the value that means "no deformation", so neutral is a position
you can feel for rather than a number you have to read. */
.rail { grid-column: 1 / -1; position: relative; height: 16px; }
.rail[data-detent]::before {
content: ""; position: absolute; left: var(--detent); top: 3px;
width: 1px; height: 10px; background: var(--rule);
}
.rail input {
-webkit-appearance: none; appearance: none;
position: relative; width: 100%; height: 16px; margin: 0; background: transparent;
}
.rail input::-webkit-slider-runnable-track { height: 2px; background: var(--rule); border-radius: 1px; }
.rail input::-webkit-slider-thumb {
-webkit-appearance: none; width: 11px; height: 11px; margin-top: -4.5px;
border: none; border-radius: 50%; background: var(--live); cursor: grab;
}
.rail input:focus { outline: none; }
.rail input:focus-visible { outline: 2px solid var(--live); outline-offset: 1px; border-radius: 3px; }
.rail input:disabled::-webkit-slider-thumb { background: var(--quiet); cursor: default; }
.readout {
margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--rule);
font-size: 11px; color: var(--quiet); min-height: 32px;
}
.readout b { color: var(--ink); font-weight: 600; font-family: var(--data); font-variant-numeric: tabular-nums; }
.plot { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.plot svg { width: 100%; height: auto; display: block; }
.plot figcaption { font: 10px/1.4 var(--data); color: var(--quiet); }
/* SVG colours live here rather than in stroke="" attributes: var() is not reliable in a
presentation attribute, and this way the drawing re-themes with the rest of the page. */
.ring, .helix, .datum { fill: none; }
.ring { stroke: var(--live); stroke-width: 1; }
.helix { stroke: var(--live); stroke-width: 1; stroke-opacity: .5; stroke-dasharray: 2 2; }
.datum { stroke: var(--quiet); stroke-width: 1; stroke-opacity: .7; }
.tick { stroke: var(--rule); stroke-width: 1; }
.plot text { fill: var(--quiet); font: 8px var(--data); }
.foot {
display: flex; align-items: center; gap: 10px;
padding: 10px 16px; border-top: 1px solid var(--rule);
}
.state { flex: 1; font-size: 11px; color: var(--quiet); }
.state.dirty { color: var(--ink); }
button {
font: 12px/1 var(--ui); padding: 6px 14px; border-radius: 4px; cursor: pointer;
border: 1px solid var(--rule); background: transparent; color: var(--ink);
}
button.go { border-color: var(--live); background: var(--live); color: var(--live-ink); }
button:disabled { opacity: .45; cursor: default; }
button:focus-visible { outline: 2px solid var(--live); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
</style>
<div class="panel">
<header class="head">
<span class="mark">Twistify</span>
<span class="head-note">Applied to every layer as it is sliced</span>
</header>
<div class="work">
<div>
<div class="rows" id="rows"></div>
<p class="readout" id="readout"></p>
</div>
<figure class="plot">
<svg id="plot" viewBox="0 0 180 208" aria-hidden="true"></svg>
<figcaption id="plotNote"></figcaption>
</figure>
</div>
<footer class="foot">
<span class="state" id="state"></span>
<button type="button" id="restore">Restore defaults</button>
<button type="button" id="save" class="go">Save</button>
</footer>
</div>
<script>
(function () {
var DEFAULTS = __ORCA_DEFAULTS__;
// Presentation only. `neutral` marks the value at which the control contributes no deformation;
// rows without one (period, floor) are shape parameters, not amounts.
var FIELDS = [
{ key: "twist_deg_per_mm", label: "Twist", unit: "\\u00b0/mm", min: -20, max: 20, step: 0.1, dp: 1, neutral: 0,
hint: "Rotation added per millimetre of height." },
{ key: "taper_per_mm", label: "Taper", unit: "/mm", min: -0.05, max: 0.05, step: 0.001, dp: 3, neutral: 0,
hint: "Scale added per millimetre. Negative narrows the model as it rises." },
{ key: "wobble_ampl_mm", label: "Wobble", unit: "mm", min: 0, max: 10, step: 0.1, dp: 1, neutral: 0,
hint: "How far each layer slides in X, from centre to peak." },
{ key: "wobble_period_mm", label: "Wobble period", unit: "mm", min: 1, max: 120, step: 1, dp: 0,
hint: "Height of one full wobble cycle." },
{ key: "min_scale", label: "Scale floor", unit: "\\u00d7", min: 0.01, max: 1, step: 0.01, dp: 2,
hint: "Taper never shrinks a layer past this, so a tall model cannot collapse." }
];
// The preview simulates this much height. It never reads the real model, so the caption says so
// rather than implying the drawing is to scale with the plate.
var Z_SPAN = 40;
var Z_STEP = 2;
var form = {};
var stored = {};
var context = (window.orca && window.orca.getContext) ? window.orca.getContext() : { scope: "global" };
var savedNoteTimer = 0;
function num(value, fallback) {
var n = parseFloat(value);
return isFinite(n) ? n : fallback;
}
function merged(config) {
var out = {};
Object.keys(DEFAULTS).forEach(function (key) {
out[key] = num(config ? config[key] : undefined, DEFAULTS[key]);
});
return out;
}
function isDirty() {
return Object.keys(DEFAULTS).some(function (key) { return form[key] !== stored[key]; });
}
// --- the transform, exactly as _layer_params() applies it -------------------------------------
function layerAt(z) {
var theta = form.twist_deg_per_mm * z * Math.PI / 180;
var scale = Math.max(form.min_scale, 1 + form.taper_per_mm * z);
var shift = 0;
if (form.wobble_ampl_mm !== 0 && form.wobble_period_mm > 0)
shift = form.wobble_ampl_mm * Math.sin(2 * Math.PI * z / form.wobble_period_mm);
return { theta: theta, scale: scale, shift: shift };
}
function deforms() {
return form.twist_deg_per_mm !== 0 || form.taper_per_mm !== 0 || form.wobble_ampl_mm !== 0;
}
// --- drawing ----------------------------------------------------------------------------------
var ISO_X = 0.866, ISO_Y = 0.5; // isometric: plan outlines stacked by height
var ORIGIN_X = 108, ORIGIN_Y = 182; // the z = 0 outline's centre, in viewBox units
var PLAN = 20; // half-width of the sample contour
var Z_UNITS = 3.3; // viewBox units per millimetre of height
var MM = 1.5; // viewBox units per millimetre of wobble travel
var REACH = 62; // how far sideways the drawing may run before it is scaled down
// Plan-view corners of the layer at `z`, before projection.
function planOutline(z) {
var layer = layerAt(z);
var half = PLAN * layer.scale;
var cos = Math.cos(layer.theta), sin = Math.sin(layer.theta);
return [[-half, -half], [half, -half], [half, half], [-half, half]].map(function (corner) {
return [corner[0] * cos - corner[1] * sin + layer.shift * MM,
corner[0] * sin + corner[1] * cos];
});
}
function points(plan, z, fit) {
return plan.map(function (p) {
var x = ORIGIN_X + (p[0] - p[1]) * ISO_X * fit;
var y = ORIGIN_Y + (p[0] + p[1]) * ISO_Y * fit - z * Z_UNITS;
return x.toFixed(1) + "," + y.toFixed(1);
}).join(" ");
}
function draw() {
var svg = [];
var layers = [];
var reach = 0;
for (var z = 0; z <= Z_SPAN + 0.001; z += Z_STEP) {
var plan = planOutline(z);
layers.push({ z: z, plan: plan });
plan.forEach(function (p) { reach = Math.max(reach, Math.abs(p[0] - p[1]) * ISO_X); });
}
// A wide taper or a big wobble would run out of the frame, so scale down to fit rather than
// clipping. Z keeps its scale, so the ruler stays true.
var fit = reach > REACH ? REACH / reach : 1;
// Z ruler: the drawing is a height plot, so it gets an axis.
for (var mark = 0; mark <= Z_SPAN; mark += 10) {
var y = ORIGIN_Y - mark * Z_UNITS;
svg.push('<line class="tick" x1="24" y1="' + y.toFixed(1) + '" x2="30" y2="' + y.toFixed(1) + '"/>');
svg.push('<text x="20" y="' + (y + 3).toFixed(1) + '" text-anchor="end">' + mark + '</text>');
}
svg.push('<text x="30" y="' + (ORIGIN_Y - Z_SPAN * Z_UNITS - 11).toFixed(1) +
'" text-anchor="end">mm</text>');
// The stack, faint at the bed and full strength at the top: the deformation grows with Z.
var trace = [];
layers.forEach(function (layer) {
var ring = points(layer.plan, layer.z, fit);
svg.push('<polygon class="ring" points="' + ring + '" stroke-opacity="' +
(0.18 + 0.82 * layer.z / Z_SPAN).toFixed(2) + '"/>');
trace.push(ring.split(" ")[1]);
});
// One corner followed all the way up: on a twisted model, the helix the walls will run in.
svg.push('<polyline class="helix" points="' + trace.join(" ") + '"/>');
// The datum: the first layer is never transformed, so it doubles as the reference outline.
svg.push('<polygon class="datum" points="' + points(layers[0].plan, 0, fit) + '"/>');
document.getElementById("plot").innerHTML = svg.join("");
document.getElementById("plotNote").textContent =
deforms() ? "Sample contour, " + Z_SPAN + " mm of Z" : "Sample contour, undeformed";
}
// --- readout ----------------------------------------------------------------------------------
function describe() {
if (!deforms())
return "No deformation. Twistify passes every layer through unchanged.";
var top = layerAt(Z_SPAN);
var parts = [];
if (form.twist_deg_per_mm !== 0)
parts.push("<b>" + (form.twist_deg_per_mm * Z_SPAN).toFixed(0) + "\\u00b0</b> of rotation");
if (form.taper_per_mm !== 0)
parts.push("<b>" + top.scale.toFixed(2) + "\\u00d7</b> scale" +
(top.scale === form.min_scale ? " (at the floor)" : ""));
if (form.wobble_ampl_mm !== 0)
parts.push("<b>\\u00b1" + form.wobble_ampl_mm.toFixed(1) + "</b> mm of wobble");
return "At " + Z_SPAN + " mm above the first layer: " + parts.join(", ") + ".";
}
function setReadout(html) { document.getElementById("readout").innerHTML = html; }
// Whatever the pointer or the keyboard is on explains itself; with neither, report the totals.
function restoreReadout() {
var id = document.activeElement ? document.activeElement.id : "";
for (var i = 0; i < FIELDS.length; i++)
if (id === "n-" + FIELDS[i].key || id === "r-" + FIELDS[i].key)
return setReadout(FIELDS[i].hint);
setReadout(describe());
}
function refreshState() {
var dirty = isDirty();
document.getElementById("save").disabled = context.readOnly || !dirty;
if (savedNoteTimer)
return; // the "Saved" note owns the line until it expires
var state = document.getElementById("state");
state.textContent = context.readOnly ? "This configuration is read-only."
: dirty ? "Unsaved changes" : "";
state.classList.toggle("dirty", dirty && !context.readOnly);
}
function announce(message) {
var state = document.getElementById("state");
state.textContent = message;
state.classList.remove("dirty");
clearTimeout(savedNoteTimer);
savedNoteTimer = setTimeout(function () { savedNoteTimer = 0; refreshState(); }, 2200);
}
// --- controls ---------------------------------------------------------------------------------
function detent(field) {
if (field.neutral === undefined) return "";
return ((field.neutral - field.min) / (field.max - field.min) * 100).toFixed(2) + "%";
}
function clamp(field, value) { return Math.min(field.max, Math.max(field.min, value)); }
// `echo` rewrites the number field with the clamped, formatted value. Off while the user is still
// typing: "-" and "0.0" are valid partial input, and rewriting them mid-keystroke fights the
// typist. The drawing still follows every keystroke.
function apply(field, value, echo) {
form[field.key] = clamp(field, value);
if (echo)
document.getElementById("n-" + field.key).value = form[field.key].toFixed(field.dp);
document.getElementById("r-" + field.key).value = form[field.key];
draw();
// A control explains itself while you approach it, but once a value moves what matters is the
// total it adds up to.
setReadout(describe());
refreshState();
}
function buildRows() {
var host = document.getElementById("rows");
FIELDS.forEach(function (field) {
var row = document.createElement("div");
row.className = "row";
// The number input is the labelled, keyboard-reachable control; the rail is the same value by
// pointer, so it stays out of the tab order and the accessibility tree.
row.innerHTML =
'<label class="row-label" for="n-' + field.key + '">' + field.label + '</label>' +
'<span class="row-figure">' +
'<input id="n-' + field.key + '" type="number" step="' + field.step + '" ' +
'min="' + field.min + '" max="' + field.max + '">' +
'<span class="unit">' + field.unit + '</span>' +
'</span>' +
'<span class="rail"' + (field.neutral === undefined ? "" : ' data-detent style="--detent:' + detent(field) + '"') + '>' +
'<input id="r-' + field.key + '" type="range" step="' + field.step + '" ' +
'min="' + field.min + '" max="' + field.max + '" tabindex="-1" aria-hidden="true">' +
'</span>';
host.appendChild(row);
var number = row.querySelector("#n-" + field.key);
var range = row.querySelector("#r-" + field.key);
number.addEventListener("input", function () { apply(field, num(number.value, form[field.key]), false); });
number.addEventListener("change", function () { apply(field, num(number.value, DEFAULTS[field.key]), true); });
range.addEventListener("input", function () { apply(field, num(range.value, form[field.key]), true); });
[number, range].forEach(function (input) {
input.addEventListener("focus", function () { setReadout(field.hint); });
input.addEventListener("mouseenter", function () { setReadout(field.hint); });
input.addEventListener("blur", restoreReadout);
input.addEventListener("mouseleave", restoreReadout);
});
});
}
function load(config) {
stored = merged(config);
form = merged(config);
FIELDS.forEach(function (field) {
document.getElementById("n-" + field.key).value = form[field.key].toFixed(field.dp);
document.getElementById("r-" + field.key).value = form[field.key];
});
draw();
restoreReadout();
refreshState();
labelRestore();
setInputsEnabled(!context.readOnly);
}
// "Restore defaults" writes the plugin's own defaults globally, but in a preset it drops that
// preset's override and falls back to the global configuration. Say which one the button does.
function labelRestore() {
var restore = document.getElementById("restore");
var preset = context.scope === "preset";
restore.textContent = preset ? "Use global settings" : "Restore defaults";
restore.title = preset ? "Discard this preset's override and use the global configuration"
: "Replace the saved configuration with Twistify's defaults";
restore.disabled = context.readOnly || (preset && !context.hasPresetOverride);
}
function setInputsEnabled(enabled) {
FIELDS.forEach(function (field) {
document.getElementById("n-" + field.key).disabled = !enabled;
document.getElementById("r-" + field.key).disabled = !enabled;
});
}
buildRows();
load(window.orca ? window.orca.getConfig() : {});
document.getElementById("save").addEventListener("click", function () {
if (!window.orca) return;
window.orca.saveConfig(form);
});
document.getElementById("restore").addEventListener("click", function () {
if (window.orca) window.orca.restoreDefaults();
});
if (window.orca && window.orca.onConfig) {
var first = true;
window.orca.onConfig(function (config) {
// Fires once immediately (already handled above) and then on every host save: reload from
// what was stored, never from what was typed.
if (first) { first = false; return; }
if (window.orca.getContext) context = window.orca.getContext();
load(config);
announce("Saved");
});
}
// No theme handler needed: every colour on the page, the drawing included, resolves through the
// --orca-* variables the host rewrites in place.
})();
</script>
"""
class Twistify(orca.slicing.SlicingPipelineCapabilityBase):
def get_name(self):
return "Twistify"
def get_default_config(self):
return _DEFAULTS
def has_config_ui(self):
return True
def get_config_ui(self):
return _CONFIG_UI.replace("__ORCA_DEFAULTS__", json.dumps(_DEFAULTS))
def execute(self, ctx):
if ctx.step != orca.slicing.Step.posSlice or ctx.object is None:
return orca.ExecutionResult.success()
p = _params(self)
if _is_identity(p):
return orca.ExecutionResult.success("Twistify: identity parameters, nothing to do")
mm_to_scaled = 1.0 / orca.slicing.unscale(1)
layers = ctx.object.layers()
if not layers:
return orca.ExecutionResult.success("Twistify: object has no layers")
# Twist/taper axis = the object's bounding-box center (scaled coords, same frame
# as the slice polygons), so each object on the plate transforms about its own
# center. Keep the float center for translate-to-origin/back around scale(), and
# a rounded-to-Point center for rotate() (which takes an integer Point).
min_x, min_y, max_x, max_y = ctx.object.bounding_box()
cx = (min_x + max_x) / 2.0
cy = (min_y + max_y) / 2.0
center = orca.host.Point(int(round(cx)), int(round(cy)))
z0 = float(layers[0].print_z) # z_rel = 0 on the first layer -> footprint untouched
layers_touched = 0
for layer in layers:
if ctx.cancelled():
break
z_rel = float(layer.print_z) - z0
theta, s, ox = _layer_params(z_rel, mm_to_scaled, p)
if theta == 0.0 and s == 1.0 and ox == 0.0:
continue # exact identity (always the first layer)
edited = False
for region in layer.regions():
for surface in region.slices.surfaces:
ex = surface.expolygon
ex.rotate(theta, center) # rotate about the object center (in place)
if s != 1.0:
# scale() scales about the coordinate ORIGIN, so re-center the
# geometry on the origin first and translate back after, making
# this a true similarity transform about the object's center.
ex.translate(-cx, -cy)
ex.scale(s)
ex.translate(cx, cy)
if ox != 0.0:
ex.translate(ox, 0.0) # wobble in X
edited = True
if edited:
# Re-derive the merged islands from the twisted region slices.
layer.make_slices()
layers_touched += 1
name = ctx.object.model_object().name or "object"
return orca.ExecutionResult.success(
f"Twistify: transformed {layers_touched} layer(s) of '{name}' "
f"(twist {p['twist_deg_per_mm']} deg/mm, taper {p['taper_per_mm']}/mm, "
f"wobble {p['wobble_ampl_mm']} mm)")
@orca.plugin
class TwistifyPackage(orca.base):
def register_capabilities(self):
orca.register_capability(Twistify)

View File

@@ -1,142 +0,0 @@
# /// script
# requires-python = ">=3.12"
#
# [tool.orcaslicer.plugin]
# name = "Twistify"
# description = "Twists, tapers, and wobbles every layer's slice polygons as a function of Z (demo)."
# author = "OrcaSlicer"
# version = "0.02"
# type = "slicing-pipeline"
# ///
"""Twistify -- twist/taper/wobble any model at slice time.
At Step.posSlice, every layer's sliced surfaces are transformed by a similarity
about the object's bounding-box center as a function of Z -- edited IN PLACE
through the host geometry classes (ExPolygon.rotate/scale/translate). Each
surface is rotated about the center, then (if tapering) translated to the
origin, uniformly scaled, and translated back, so the taper stays centered on
the object instead of drifting toward the coordinate origin. An optional X
wobble is applied last. After the per-region edits, layer.make_slices()
re-derives the layer's merged islands so overhang/bridge/skirt/support stay
coherent. The split slice loop runs make_perimeters() right after the hook, so
the transform cascades into perimeters, infill, and the final G-code -- the
preview corkscrews and the print keeps correct walls/infill/flow.
Because we edit geometry in place, surface types are preserved automatically
(no per-surface type carry needed), and no numpy is required --
rotate/scale/translate are host methods. Parameters come from self.get_config()
(see _params below), which the host seeds from get_default_config(). The first
object layer is untouched (z_rel = 0), so bed adhesion is unaffected.
"""
import math
import json
import orca
_DEFAULTS = {
"twist_deg_per_mm": 1.0,
"taper_per_mm": 0.0,
"wobble_ampl_mm": 0.0,
"wobble_period_mm": 20.0,
"min_scale": 0.05,
}
def _params(self):
try:
src = json.loads(self.get_config())
except (AttributeError, TypeError):
src = {}
out = {}
for key, default in _DEFAULTS.items():
try:
out[key] = float(src[key])
except (KeyError, TypeError, ValueError):
out[key] = default
return out
def _is_identity(p):
return p["twist_deg_per_mm"] == 0.0 and p["taper_per_mm"] == 0.0 and p["wobble_ampl_mm"] == 0.0
def _layer_params(z_rel, mm_to_scaled, p):
"""(angle_rad, scale, x_offset_scaled) for one layer. Exact identity at z_rel == 0."""
theta = math.radians(p["twist_deg_per_mm"] * z_rel)
s = max(p["min_scale"], 1.0 + p["taper_per_mm"] * z_rel)
ox = 0.0
if p["wobble_ampl_mm"] != 0.0 and p["wobble_period_mm"] > 0.0:
ox = p["wobble_ampl_mm"] * math.sin(2.0 * math.pi * z_rel / p["wobble_period_mm"]) * mm_to_scaled
return theta, s, ox
class Twistify(orca.slicing.SlicingPipelineCapabilityBase):
def get_name(self):
return "Twistify"
def get_default_config(self):
return _DEFAULTS
def execute(self, ctx):
if ctx.step != orca.slicing.Step.posSlice or ctx.object is None:
return orca.ExecutionResult.success()
p = _params(self)
if _is_identity(p):
return orca.ExecutionResult.success("Twistify: identity parameters, nothing to do")
mm_to_scaled = 1.0 / orca.slicing.unscale(1)
layers = ctx.object.layers()
if not layers:
return orca.ExecutionResult.success("Twistify: object has no layers")
# Twist/taper axis = the object's bounding-box center (scaled coords, same frame
# as the slice polygons), so each object on the plate transforms about its own
# center. Keep the float center for translate-to-origin/back around scale(), and
# a rounded-to-Point center for rotate() (which takes an integer Point).
min_x, min_y, max_x, max_y = ctx.object.bounding_box()
cx = (min_x + max_x) / 2.0
cy = (min_y + max_y) / 2.0
center = orca.host.Point(int(round(cx)), int(round(cy)))
z0 = float(layers[0].print_z) # z_rel = 0 on the first layer -> footprint untouched
layers_touched = 0
for layer in layers:
if ctx.cancelled():
break
z_rel = float(layer.print_z) - z0
theta, s, ox = _layer_params(z_rel, mm_to_scaled, p)
if theta == 0.0 and s == 1.0 and ox == 0.0:
continue # exact identity (always the first layer)
edited = False
for region in layer.regions():
for surface in region.slices.surfaces:
ex = surface.expolygon
ex.rotate(theta, center) # rotate about the object center (in place)
if s != 1.0:
# scale() scales about the coordinate ORIGIN, so re-center the
# geometry on the origin first and translate back after, making
# this a true similarity transform about the object's center.
ex.translate(-cx, -cy)
ex.scale(s)
ex.translate(cx, cy)
if ox != 0.0:
ex.translate(ox, 0.0) # wobble in X
edited = True
if edited:
# Re-derive the merged islands from the twisted region slices.
layer.make_slices()
layers_touched += 1
name = ctx.object.model_object().name or "object"
return orca.ExecutionResult.success(
f"Twistify: transformed {layers_touched} layer(s) of '{name}' "
f"(twist {p['twist_deg_per_mm']} deg/mm, taper {p['taper_per_mm']}/mm, "
f"wobble {p['wobble_ampl_mm']} mm)")
@orca.plugin
class TwistifyPackage(orca.base):
def register_capabilities(self):
orca.register_capability(Twistify)

View File

@@ -306,6 +306,12 @@ modules:
sha256: c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684
dest: external-packages/python3
# wxInspector 1.0.0
- type: file
url: https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip
sha256: 0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7
dest: external-packages/wxInspector
# ---------------------------------------------------------------
# Fallback archives for deps normally provided by the GNOME SDK.
# These are only used if find_package() fails to locate them.

View File

@@ -2,11 +2,13 @@
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
IgnorableNamespaces="uap rescap rescap3 desktop6 virtualization">
IgnorableNamespaces="uap uap3 rescap rescap3 desktop desktop6 virtualization">
<Identity Name="@MSIX_IDENTITY_NAME@"
Publisher="@MSIX_PUBLISHER@"
@@ -64,6 +66,20 @@
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="orcaslicer" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="prusaslicer" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="bambustudio" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="cura" />
</uap:Extension>
<uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication">
<uap3:AppExecutionAlias>
<desktop:ExecutionAlias Alias="orca-slicer.exe" />
</uap3:AppExecutionAlias>
</uap3:Extension>
</Extensions>
</Application>
</Applications>

View File

@@ -23,7 +23,7 @@ if %FULL_MODE%==1 (
call :prepareGettextList "%list_file%" "%filtered_list%" "%missing_list%"
if "!has_sources!"=="1" (
if not exist "%generated_i18n%" mkdir "%generated_i18n%"
.\tools\xgettext.exe --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_CTX:1,2c --keyword=_CTX_utf8:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "%filtered_list%" -o "%generated_pot%"
.\tools\xgettext.exe --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_CONTEXT:1,2c --keyword=_u8L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "%filtered_list%" -o "%generated_pot%"
if errorlevel 1 (
set "script_exit_code=1"
) else (

View File

@@ -85,7 +85,7 @@ if $FULL_MODE; then
generated_pot_file="${generated_i18n_dir}/OrcaSlicer.pot"
mkdir -p "$generated_i18n_dir"
xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_CTX:1,2c --keyword=_CTX_utf8:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "$filtered_list" -o "$generated_pot_file"
xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_CONTEXT:1,2c --keyword=_u8L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "$filtered_list" -o "$generated_pot_file"
python3 scripts/HintsToPot.py ./resources "$generated_i18n_dir"
if [ -f "$pot_file" ] && files_equal_ignoring_pot_date "$pot_file" "$generated_pot_file"; then

View File

@@ -88,6 +88,10 @@ if (SLIC3R_GUI)
list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL)
# list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
find_package(wxInspector REQUIRED)
list(APPEND wxWidgets_LIBRARIES "wxInspector::wxInspector")
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
add_subdirectory(slic3r)
@@ -283,10 +287,6 @@ else ()
COMMAND ln -sf OrcaSlicer orca-slicer
WORKING_DIRECTORY "$<TARGET_FILE_DIR:OrcaSlicer>"
VERBATIM)
else ()
add_custom_command(TARGET OrcaSlicer POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:OrcaSlicer>"
VERBATIM)
endif ()
if (XCODE)
# Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level

View File

@@ -24,6 +24,8 @@
#include <iostream>
#include <math.h>
#include <csignal>
#include <atomic>
#include <new>
#if defined(__linux__) || defined(__LINUX__)
#include <condition_variable>
@@ -7608,6 +7610,9 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
}*/
#if defined(_MSC_VER) || defined(__MINGW32__)
// Guards against a failed allocation inside the dump re-entering the new-handler.
static std::atomic<bool> g_dump_in_progress{false};
extern "C" {
__declspec(dllexport) int __stdcall orcaslicer_main(int argc, wchar_t **argv)
{
@@ -7626,10 +7631,22 @@ extern "C" {
//AddVectoredExceptionHandler(1, CBaseException::UnhandledExceptionFilter);
SET_DEFULTER_HANDLER();
#endif
// Dump before unwinding, while the stack still names what asked for the memory. Throwing
// std::bad_alloc is standard-permitted here and is what reaches generic_exception_handle().
std::set_new_handler([]() {
int *a = nullptr;
*a = 0;
});
if (!g_dump_in_progress.exchange(true)) {
try {
// A null EXCEPTION_POINTERS walks the calling thread as it stands.
CBaseException base(GetCurrentProcess(), GetCurrentProcessId(), NULL, nullptr);
base.ShowCallstack();
} catch (...) {
// A failed dump must not displace the std::bad_alloc owed to the caller.
}
// ObjParser recovers from std::bad_alloc, so let a later one dump again.
g_dump_in_progress = false;
}
throw std::bad_alloc();
});
// Call the UTF8 main.
return CLI().run(argc, argv_ptrs.data());
}

View File

@@ -202,10 +202,25 @@ void AppConfig::set_defaults()
if (get("seq_top_layer_only").empty())
set("seq_top_layer_only", "1");
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers the preview layer slider is not scrubbed to
if (get("preview_dim_previous_layers").empty())
set_bool("preview_dim_previous_layers", false);
// ORCA: brightness of those dimmed layers, in percent. 0 = black, capped at 99 because
// 100 would render them unchanged, which is what disabling the option already does
if (get("preview_dim_previous_layers_brightness").empty())
set("preview_dim_previous_layers_brightness", "40");
else {
int brightness = 40;
try {
brightness = std::stoi(get("preview_dim_previous_layers_brightness"));
}
catch (...) {
brightness = 40;
}
set("preview_dim_previous_layers_brightness", std::to_string(std::max(0, std::min(brightness, 99))));
}
if (get("filaments_area_preferred_count").empty())
set("filaments_area_preferred_count", "10");

View File

@@ -25,7 +25,7 @@ public:
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It>>
BoundingBoxBase(It from, It to)
BoundingBoxBase(It from, It to) : BoundingBoxBase()
{ construct(*this, from, to); }
BoundingBoxBase(const PointsType &points)

View File

@@ -32,15 +32,13 @@ static void append_and_translate(ExPolygons &dst, const ExPolygons &src, const P
for (; dst_idx < dst.size(); ++dst_idx)
dst[dst_idx].translate(instance_shift);
}
// BBS: generate brim area by objs
static void append_and_translate(ExPolygons& dst, const ExPolygons& src,
const PrintInstance& instance, size_t instance_idx, std::map<ObjectInstanceID, ExPolygons>& brimAreaMap) {
// Orca: Translate the brim area into print coordinates and store it per instance.
static void append_and_translate(const ExPolygons& src, const PrintInstance& instance,
size_t instance_idx, std::map<ObjectInstanceID, ExPolygons>& brimAreaMap) {
ExPolygons srcShifted = src;
Point instance_shift = instance.shift_without_plate_offset();
for (size_t src_idx = 0; src_idx < srcShifted.size(); ++src_idx)
srcShifted[src_idx].translate(instance_shift);
srcShifted = diff_ex(srcShifted, dst);
//expolygons_append(dst, temp2);
for (ExPolygon& expoly : srcShifted)
expoly.translate(instance_shift);
expolygons_append(brimAreaMap[{ instance.print_object->id(), instance_idx }], std::move(srcShifted));
}
@@ -572,7 +570,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
for (size_t instance_idx = 0; instance_idx < object->instances().size(); ++instance_idx) {
const PrintInstance& instance = object->instances()[instance_idx];
if (!brim_area_object.empty())
append_and_translate(brim_area, brim_area_object, instance, instance_idx, brimAreaMap);
append_and_translate(brim_area_object, instance, instance_idx, brimAreaMap);
append_and_translate(no_brim_area, no_brim_area_object, instance);
append_and_translate(holes, holes_object, instance);
append_and_translate(objectIslands, objectIsland, instance);
@@ -875,6 +873,14 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
ExPolygons islands_area_ex = outer_inner_brim_area(print,
float(flow.scaled_spacing()), brimAreaMap, objPrintVec, printExtruders);
if (!print.config().combine_brims) {
ExPolygons claimed_area;
for (auto& [_, areas] : brimAreaMap) {
areas = diff_ex(areas, claimed_area);
expolygons_append(claimed_area, areas);
}
}
// BBS: Find boundingbox of the first layer
for (const ObjectID printObjID : print.print_object_ids()) {
BoundingBox bbx;

View File

@@ -19,6 +19,7 @@ if (TARGET OpenVDB::openvdb)
endif()
option(BUILD_SHARED_LIBS "Build shared libs" OFF)
option(USE_SLIC3R_CONSOLE_LOG "Enable console logging in RelWithDebInfo builds" OFF)
set(lisbslic3r_sources
AABBMesh.cpp
@@ -247,6 +248,8 @@ set(lisbslic3r_sources
GCode/Thumbnails.hpp
GCode/ToolOrdering.cpp
GCode/ToolOrdering.hpp
GCode/OrderingStrategies.cpp
GCode/OrderingStrategies.hpp
GCode/WipeTower2.cpp
GCode/WipeTower2.hpp
GCode/WipeTower.cpp
@@ -441,8 +444,6 @@ set(lisbslic3r_sources
Tesselate.cpp
Tesselate.hpp
TextConfiguration.hpp
TextureDisplacement.cpp
TextureDisplacement.hpp
Thread.cpp
Thread.hpp
Time.cpp
@@ -499,8 +500,12 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
cmake_policy(PUSH)
cmake_policy(SET CMP0011 NEW)
# CGAL's config resets policies (cmake_minimum_required ...3.23), so a plain SET
# can't reach it; the default opts its Boost lookup into BoostConfig (CMP0167).
set(CMAKE_POLICY_DEFAULT_CMP0167 NEW)
find_package(CGAL REQUIRED)
find_package(OpenCV REQUIRED core)
unset(CMAKE_POLICY_DEFAULT_CMP0167)
cmake_policy(POP)
add_library(libslic3r_cgal STATIC
@@ -537,7 +542,9 @@ endif ()
encoding_check(libslic3r)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
target_compile_definitions(libslic3r PRIVATE $<$<CONFIG:RelWithDebInfo>:SLIC3R_CONSOLE_LOG>)
if (USE_SLIC3R_CONSOLE_LOG)
target_compile_definitions(libslic3r PRIVATE $<$<CONFIG:RelWithDebInfo>:SLIC3R_CONSOLE_LOG>)
endif()
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(libslic3r SYSTEM PUBLIC ${EXPAT_INCLUDE_DIRS})
@@ -547,7 +554,7 @@ find_package(OpenCASCADE REQUIRED)
target_include_directories(libslic3r SYSTEM PUBLIC ${OpenCASCADE_INCLUDE_DIR})
find_package(JPEG REQUIRED)
find_package(draco REQUIRED)
find_package(Draco REQUIRED)
set(OCCT_LIBS
TKXDESTEP

View File

@@ -2,6 +2,7 @@
#define slic3r_Config_hpp_
#include <assert.h>
#include <algorithm>
#include <map>
#include <climits>
#include <cfloat>
@@ -780,10 +781,14 @@ public:
this->values[i] = rhs_vec->values[i];
modified = true;
} else {
if ((i < default_index.size()) && (default_index[i] < default_value.size()))
// Orca: a negative slot (failed variant lookup) must not silently collapse the
// whole array to the first slot's value — the int-vs-size_t comparison used to
// promote -1 past the bounds check. Keep the slot's own value (get_at-style
// clamp) when no valid index is available.
if ((i < default_index.size()) && (default_index[i] >= 0) && (size_t(default_index[i]) < default_value.size()))
this->values[i] = default_value[default_index[i]];
else
this->values[i] = default_value[0];
this->values[i] = default_value[std::min(i, default_value.size() - 1)];
}
}
return modified;
@@ -2106,6 +2111,11 @@ public:
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = rhs->getInt();
// Orca: options embedded in a StaticPrintConfig are constructed without a keys_map;
// adopt the source's so a later serialize() can emit names.
if (this->keys_map == nullptr)
if (auto rhs_generic = dynamic_cast<const ConfigOptionEnumGeneric *>(rhs))
this->keys_map = rhs_generic->keys_map;
}
std::string serialize() const override
@@ -2162,7 +2172,12 @@ public:
if (rhs->type() != this->type())
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumsGeneric
this->values = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs)->values;
auto rhs_enums = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs);
this->values = rhs_enums->values;
// Orca: options embedded in a StaticPrintConfig are constructed without a keys_map;
// adopt the source's so a later serialize() emits names instead of empty tokens.
if (this->keys_map == nullptr)
this->keys_map = rhs_enums->keys_map;
}
std::string serialize() const override

View File

@@ -57,6 +57,9 @@ double calculate_infill_rotation_angle(const PrintObject* object,
if (template_string.empty()) {
return Geometry::deg2rad(fixed_infill_angle);
}
// Convert the id to an index. Layer::id() counts the raft layers, object->layers() does not.
const size_t first_object_layer_id = object->get_layer(0)->id();
layer_id = layer_id > first_object_layer_id ? layer_id - first_object_layer_id : 0;
double angle = 0.0;
ConfigOptionFloats rotate_angles;
const std::string search_string = "/NnZz$LlUuQq~^|#";
@@ -75,6 +78,8 @@ double calculate_infill_rotation_angle(const PrintObject* object,
double angle_start = 0;
double limit_fill_z = object->get_layer(0)->bottom_z();
double start_fill_z = limit_fill_z;
// The raft height, or 0 without a raft.
const double print_z_offset = object->slicing_parameters().object_print_z_min;
bool _noop = false;
auto fill_form = std::string::npos;
bool _absolute = false;
@@ -84,7 +89,8 @@ double calculate_infill_rotation_angle(const PrintObject* object,
for (int i = 0; i <= layer_id; i++) {
double fill_z = object->get_layer(i)->bottom_z();
if (limit_fill_z < object->get_layer(i)->slice_z) {
// slice_z is measured from the bottom of the model, limit_fill_z from the build plate.
if (limit_fill_z < object->get_layer(i)->slice_z + print_z_offset) {
if (repeats) { // if repeats >0 then restore parameters for new iteration
limit_fill_z += limit_fill_z - start_fill_z;
start_fill_z = fill_z;

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