mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 11:21:30 +00:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4a18c633e | ||
|
|
7707487252 | ||
|
|
3df9c215f8 | ||
|
|
cda1588578 | ||
|
|
ec0d8c225f | ||
|
|
3040ebaac1 | ||
|
|
5298e49dd2 | ||
|
|
4964f49765 | ||
|
|
ea471dc82d | ||
|
|
bd65cce3f4 | ||
|
|
d976d9eb0c | ||
|
|
d347a80ef8 | ||
|
|
e340a13c18 | ||
|
|
9e16cdb23b | ||
|
|
58842bab05 | ||
|
|
04204ec0d3 | ||
|
|
ae27a795b6 | ||
|
|
ecd0be353c | ||
|
|
6a07853933 | ||
|
|
5ae9015c82 | ||
|
|
0ddc730854 | ||
|
|
ea280ba6f6 | ||
|
|
237cd10eb5 | ||
|
|
e77d179bbe | ||
|
|
d5aaa463c8 | ||
|
|
8c03985818 | ||
|
|
93b58a2034 | ||
|
|
6be6fdd7c7 | ||
|
|
521a30a45c | ||
|
|
9ed459e132 |
@@ -12,9 +12,9 @@ name: Daily OFL OTA Update
|
||||
# vendor-dispatch path is also what makes post_merge_profiles.yml call the OTA auto-publish API after
|
||||
# uploading - see post_merge_profiles.yml for both sides of that contract.
|
||||
#
|
||||
# If at least one branch was dispatched this run, a final step clears OFL's pending-publish
|
||||
# table (POST /api/v1/ota/ofl/pending/clear) - the daily "published everything, reset" signal.
|
||||
# That table is populated only by this pipeline's own auto-publish calls.
|
||||
# At the start of each run, the pending-publish table is cleared up to a captured
|
||||
# timestamp (POST /api/v1/ota/ofl/pending/clear?timestamp=...). Changes merged after
|
||||
# that timestamp remain pending for the next run.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -34,6 +34,32 @@ jobs:
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Capture start timestamp and clear OFL pending queue
|
||||
id: start
|
||||
shell: bash
|
||||
env:
|
||||
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
|
||||
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
|
||||
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
|
||||
|
||||
timestamp="$(date -u +%s)"
|
||||
echo "timestamp=$timestamp" >> "$GITHUB_OUTPUT"
|
||||
|
||||
resp_file="$RUNNER_TEMP/ota-pending-clear-response.json"
|
||||
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
|
||||
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending/clear?timestamp=$timestamp" \
|
||||
-H "Authorization: Bearer $OTA_API_KEY")"
|
||||
body="$(cat "$resp_file")"
|
||||
echo "$body"
|
||||
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::OTA pending-clear call failed with HTTP $status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
@@ -46,13 +72,12 @@ jobs:
|
||||
run: git fetch origin '+refs/heads/*:refs/remotes/origin/*'
|
||||
|
||||
- name: Scan branches and publish changed OFL profiles
|
||||
id: scan
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SCAN_UNTIL: ${{ steps.start.outputs.timestamp }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
published_any=false
|
||||
|
||||
mapfile -t branches < <(
|
||||
gh api "repos/${{ github.repository }}/branches" --paginate --jq '.[].name' \
|
||||
@@ -62,85 +87,53 @@ jobs:
|
||||
for branch in "${branches[@]}"; do
|
||||
echo "::group::$branch"
|
||||
|
||||
# Use this workflow's own last successful run as the checkpoint. A
|
||||
# successful post_merge_profiles.yml run may record an OFL merge as
|
||||
# pending without publishing OFL, so its history must not advance
|
||||
# this scan's checkpoint. Keep the branch filter aligned with the
|
||||
# branch being inspected so each branch has its own checkpoint.
|
||||
# A failed daily run naturally gets retried from the previous
|
||||
# successful daily checkpoint; a branch with no prior run is
|
||||
# treated as changed below.
|
||||
# post_merge_profiles.yml's own run history, not this workflow's: this
|
||||
# workflow only ever runs against main (schedule, or workflow_dispatch
|
||||
# --ref main), so its head branch never varies - filtering ITS history
|
||||
# by $branch would never match anything except main. post_merge_profiles.yml
|
||||
# genuinely runs per-branch (this dispatch below sets --ref "$branch"),
|
||||
# so its history is the real per-branch checkpoint. It also means a
|
||||
# failed publish naturally gets retried tomorrow: the checkpoint only
|
||||
# advances on a run that actually succeeded.
|
||||
# --method GET is required, not cosmetic: gh api defaults to POST
|
||||
# whenever -f fields are present unless a method is given
|
||||
# explicitly, and POST on this list-runs endpoint 404s - confirmed
|
||||
# on real Actions infrastructure, not just reasoned about.
|
||||
since="$(gh api --method GET "repos/${{ github.repository }}/actions/workflows/ofl-ota-cronjob.yml/runs" \
|
||||
since="$(gh api --method GET "repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
|
||||
-f status=success -f branch="$branch" -f per_page=1 \
|
||||
--jq '.workflow_runs[0].run_started_at // empty')"
|
||||
|
||||
if [ -z "$since" ]; then
|
||||
echo "No prior successful run for $branch; treating OFL as changed."
|
||||
changed=true
|
||||
else
|
||||
changed_files="$(git log --since="$since" --name-only --pretty=format: "origin/$branch" -- \
|
||||
echo "No prior successful run for $branch; checking OFL changes up to $SCAN_UNTIL."
|
||||
changed_files="$(git log --until="$SCAN_UNTIL" --name-only --pretty=format: "origin/$branch" -- \
|
||||
resources/profiles/OrcaFilamentLibrary resources/profiles/OrcaFilamentLibrary.json \
|
||||
| sed '/^$/d')"
|
||||
if [ -n "$changed_files" ]; then
|
||||
echo "OFL changed on $branch since $since:"
|
||||
echo "$changed_files"
|
||||
changed=true
|
||||
else
|
||||
echo "No OFL changes on $branch since $since."
|
||||
changed=false
|
||||
fi
|
||||
else
|
||||
changed_files="$(git log --since="$since" --until="$SCAN_UNTIL" --name-only --pretty=format: "origin/$branch" -- \
|
||||
resources/profiles/OrcaFilamentLibrary resources/profiles/OrcaFilamentLibrary.json \
|
||||
| sed '/^$/d')"
|
||||
fi
|
||||
|
||||
if [ -n "$changed_files" ]; then
|
||||
echo "OFL changed on $branch from ${since:-the beginning} through $SCAN_UNTIL:"
|
||||
echo "$changed_files"
|
||||
changed=true
|
||||
else
|
||||
echo "No OFL changes on $branch through $SCAN_UNTIL."
|
||||
changed=false
|
||||
fi
|
||||
|
||||
if [ "$changed" = true ]; then
|
||||
# Tolerate a per-branch failure (e.g. a pre-existing release branch
|
||||
# whose post_merge_profiles.yml predates the vendor/auto_publish
|
||||
# inputs) rather than aborting the whole scan under set -e.
|
||||
if gh workflow run post_merge_profiles.yml \
|
||||
if ! gh workflow run post_merge_profiles.yml \
|
||||
--repo "${{ github.repository }}" \
|
||||
--ref "$branch" \
|
||||
-f vendor="$VENDOR" -f auto_publish=true; then
|
||||
published_any=true
|
||||
else
|
||||
echo "::warning::failed to dispatch post_merge_profiles.yml for $branch - its post_merge_profiles.yml at this ref may predate the vendor/auto_publish inputs"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
echo "published_any=$published_any" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Clear OFL pending queue
|
||||
# Only when this run actually kicked off at least one publish - the
|
||||
# daily reset is scoped to today's real activity, not called on a day
|
||||
# where every branch reported no changes. Note "published_any" reflects
|
||||
# a successful DISPATCH, not a confirmed live publish: gh workflow run
|
||||
# is fire-and-forget, so this workflow never learns whether the
|
||||
# dispatched post_merge_profiles.yml run actually reached its own
|
||||
# auto-publish call. Acceptable since the table is populated only by
|
||||
# our own auto-publish calls, not by anything else.
|
||||
if: steps.scan.outputs.published_any == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
|
||||
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
|
||||
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
|
||||
|
||||
resp_file="$RUNNER_TEMP/ota-pending-clear-response.json"
|
||||
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
|
||||
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending/clear" \
|
||||
-H "Authorization: Bearer $OTA_API_KEY")"
|
||||
body="$(cat "$resp_file")"
|
||||
echo "$body"
|
||||
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::OTA pending-clear call failed with HTTP $status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+7
-2
@@ -56,9 +56,9 @@ You can do this in Environment Variables settings.
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
# if CMAKE_OSX_DEPLOYMENT_TARGET is not set, set it to 11.3
|
||||
# if CMAKE_OSX_DEPLOYMENT_TARGET is not set, set it to 12.0 (the lowest Xcode 27 accepts)
|
||||
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OS X deployment version" FORCE)
|
||||
endif ()
|
||||
message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
endif ()
|
||||
@@ -279,6 +279,11 @@ if (APPLE)
|
||||
endif()
|
||||
SET(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.orcaslicer.OrcaSlicer")
|
||||
|
||||
# The macOS CI jobs build with Ninja (build_release_macos.sh -x), so the Xcode generator
|
||||
# is not covered. Xcode adds -Wshorten-64-to-32 by default ("Implicit Conversion to 32 Bit
|
||||
# Type"); Ninja/-Wall does not, and under -Werror it fails Xcode builds on code CI accepts.
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_64_TO_32_BIT_CONVERSION "NO")
|
||||
|
||||
message(STATUS "Orca: IS_CROSS_COMPILE: ${IS_CROSS_COMPILE}")
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
|
||||
@@ -53,7 +53,7 @@ while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
|
||||
echo " -s: Build slicer only"
|
||||
echo " -u: Build universal app only (requires existing arm64 and x86_64 app bundles)"
|
||||
echo " -n: Nightly build"
|
||||
echo " -t: Specify minimum version of the target platform, default is 11.3"
|
||||
echo " -t: Specify minimum version of the target platform, default is 12.0"
|
||||
echo " -x: Use Ninja Multi-Config CMake generator, default is Xcode"
|
||||
echo " -b: Build without reconfiguring CMake"
|
||||
echo " -c: Set CMake build configuration, default is Release"
|
||||
@@ -95,7 +95,7 @@ if [ -z "$DEPS_CMAKE_GENERATOR" ]; then
|
||||
fi
|
||||
|
||||
if [ -z "$OSX_DEPLOYMENT_TARGET" ]; then
|
||||
export OSX_DEPLOYMENT_TARGET="11.3"
|
||||
export OSX_DEPLOYMENT_TARGET="12.0"
|
||||
fi
|
||||
|
||||
if [ -z "$CMAKE_IGNORE_PREFIX_PATH" ]; then
|
||||
|
||||
Vendored
+2
-2
@@ -26,9 +26,9 @@ endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
if (APPLE)
|
||||
# if CMAKE_OSX_DEPLOYMENT_TARGET is not set, set it to 11.3
|
||||
# if CMAKE_OSX_DEPLOYMENT_TARGET is not set, set it to 12.0 (the lowest Xcode 27 accepts)
|
||||
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OS X deployment version" FORCE)
|
||||
endif ()
|
||||
message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
|
||||
|
||||
@@ -2,219 +2,136 @@
|
||||
|
||||
## Why it exists
|
||||
|
||||
The main window is a notebook of tabs. When the first frame appears, one tab is on
|
||||
screen and the others are not; some of them are opened later in the session, some
|
||||
never, and some only exist for certain printers. Building a tab before the first frame
|
||||
adds its cost to every startup, whether or not the tab is used.
|
||||
The main window is a notebook of tabs, and only one of them is on screen when the first
|
||||
frame appears. Others are opened later in the session, some never, and some only exist
|
||||
for certain printers. Building every tab before the first frame makes each startup pay for
|
||||
tabs the user may never open.
|
||||
|
||||
This subsystem builds a tab's panel the first time the tab is shown. It also builds the
|
||||
remaining tabs while the user is idle after startup, in units of tens of milliseconds,
|
||||
so a click that lands in the middle of one waits for that unit and no longer. Startup
|
||||
pays only for what the first frame shows, and the other tabs are usually built before
|
||||
anyone opens them.
|
||||
This subsystem builds a tab the first time it is shown, and builds the rest in small units
|
||||
while the user is idle after startup. Startup pays only for what the first frame shows,
|
||||
the other tabs are usually ready before anyone opens them, and a click that lands in the
|
||||
middle of the idle build waits for one unit at most. Work that is not a tab, such as a
|
||||
dialog or the 3D view's GL resources, uses the same machinery.
|
||||
|
||||
## The parts
|
||||
|
||||
`src/slic3r/GUI/Lazy.hpp`, `LazyPage.hpp`, `StagedBuild.hpp` and `IdleScheduler.hpp/.cpp`
|
||||
(with its wx-free `PrebuildQueue.hpp`) are independent. A holder can hold anything a
|
||||
factory makes, a page is a holder with a placeholder widget, a staged object can live
|
||||
outside a holder, and the scheduler knows none of them; it runs tasks, which `MainFrame`
|
||||
makes from the holders.
|
||||
The parts are independent. A holder can hold anything a factory makes, a placeholder page
|
||||
is a holder with a widget, a staged object can live outside a holder, and the scheduler
|
||||
knows none of them; it runs tasks, which the main window makes from the holders.
|
||||
|
||||
### Lazy<T>: the holder
|
||||
### The holder: `Lazy<T>`
|
||||
|
||||
Holds an object that a factory makes on first use, under a name for the log and a place
|
||||
in the idle queue, both given by the owner. The header has no wx dependency; the busy
|
||||
cursor for an on-demand build lives in `Lazy.cpp` and is skipped when there is no app,
|
||||
so the holder is unit-tested.
|
||||
A holder keeps one object and the factory that makes it. The rest of the app reads the
|
||||
object if it exists, makes sure it exists now when about to show or navigate to it, or runs
|
||||
something once it exists. A type with one instance in the app gets these as statics
|
||||
through `LazyInstance<T>`, so callers need no reference to the main window, and all of them
|
||||
are harmless while no holder exists.
|
||||
|
||||
- `get()` is null until the object is completely built; `built()` says the same.
|
||||
- `ensure()` builds whatever is left now, under a busy cursor, and returns the object, or
|
||||
null in the two cases below. A click on an unbuilt tab or a first open of a dialog goes
|
||||
through this, and it logs the units and time it took.
|
||||
- `build_step()` runs one unit of construction and returns true while more remain. The
|
||||
first unit is the factory call, and each later unit is one `StagedBuild` step if the
|
||||
type has them.
|
||||
- `when_built(fn)` runs `fn(object)` now if it exists, otherwise once it is built.
|
||||
- `pending()` says whether the idle prebuild has work here: not built, and the factory
|
||||
has not returned null. A null factory result is logged and the holder stays unbuilt.
|
||||
- A unit that pumps the event loop cannot re-enter the holder; a nested `build_step()`
|
||||
does nothing and a nested `ensure()` returns null.
|
||||
- After a unit throws, the holder and the scheduler still run the next one.
|
||||
The holder guarantees that callers see the object only once it is completely built. A
|
||||
build cannot re-enter itself, so a nested request finds nothing yet, and a factory that
|
||||
returns null or a unit that throws leaves the holder and the scheduler able to carry on.
|
||||
The holder does not own the object; its wx parent does, as for any window. The holder has
|
||||
no wx dependency and is unit-tested.
|
||||
|
||||
The holder does not own the object; its wx parent does, as for any window. A type with
|
||||
one instance in the app derives from `LazyInstance<T>`, which points at that instance's
|
||||
holder (the holder registers itself, and a recreated `MainFrame`'s holder replaces the
|
||||
old frame's) and gives the type the static entry points the rest of the app uses,
|
||||
`T::if_built()`, `T::ensure()` and `T::when_built(fn)`. They return null, or do
|
||||
nothing, while no holder exists, so a caller needs no `mainframe` check.
|
||||
### The placeholder page: `LazyPage<Panel>`
|
||||
|
||||
`built()` is an atomic flag, since a job worker reads it through the statics
|
||||
(`MainFrame::get_calibration_curr_tab()`); it is set after the object is complete.
|
||||
The notebook needs a page object for a tab to exist and for tabs to be inserted and
|
||||
removed by pointer, and the placeholder is that object. It builds the real panel inside
|
||||
itself the first time it is shown and forwards showing and hiding afterwards, so a panel's
|
||||
own show handling stays its activation hook. Nothing builds while the main window is
|
||||
hidden; the window's first show builds the start page. A page that is out of the book is
|
||||
not prebuilt. A panel built while its page is hidden stays hidden, and gets the theming
|
||||
the window applied before the panel existed.
|
||||
|
||||
`LazyBase` is the holder's type-free interface (`name()`, `built()`, `pending()`,
|
||||
`build_step()`, `prebuild_order()`) and is what the scheduler side sees.
|
||||
### Staged construction: `StagedBuild`
|
||||
|
||||
### LazyPage<Panel>: the placeholder
|
||||
A constructor too big to be one unit builds a skeleton and queues the rest as steps, which
|
||||
run one per unit. A child panel's steps can be forwarded to its parent, and the parent is
|
||||
complete only once the child is. Nothing may use what a step builds before the last step
|
||||
has run, so staged panels follow these constraints:
|
||||
|
||||
A `wxPanel` placed in the parent in place of the real panel, and a `Lazy<Panel>` whose
|
||||
factory makes the panel inside it (by default `new Panel(parent)`). The notebook needs a
|
||||
page object for the tab to exist and for `show_device()` to insert and remove tabs by
|
||||
pointer, and the placeholder is that object. `MainFrame` creates every page once, named
|
||||
after its `TAB_ID_*`, and keeps it for the frame's life; `show_device()` only moves
|
||||
pages in and out of the book.
|
||||
|
||||
- `Show()` is forwarded to the panel, so a panel's own `Show()` override stays its
|
||||
activation hook (refresh timers, machine sync) and `SelectPageByName()` works
|
||||
unchanged. A show builds the panel unless the frame itself is still hidden, because
|
||||
the book selects its first page as it is inserted; `MainFrame::Show()` shows the
|
||||
current page again when the frame becomes visible, which builds it.
|
||||
- `in_book()` says whether the parent notebook currently lists the page, and
|
||||
`pending()` is that and not built, so a tab `show_device()` has taken out of the book
|
||||
is not prebuilt.
|
||||
- A panel built while its page is hidden stays hidden, and a completed panel gets the
|
||||
dark-mode pass the frame ran before it existed.
|
||||
|
||||
The Compare presets dialog is a holder without a page. `MainFrame` keeps a
|
||||
`Lazy<DiffPresetDialog>` whose factory constructs the dialog and binds its events, the
|
||||
dialog derives from `LazyInstance`, and its callers use
|
||||
`DiffPresetDialog::ensure()->show()` and `DiffPresetDialog::if_built()` like a tab's
|
||||
callers do. Saving a preset refreshes the dialog only while it is shown, since `show()`
|
||||
reloads the presets.
|
||||
|
||||
### StagedBuild: construction in units
|
||||
|
||||
A mixin for a panel whose constructor is too big to be one unit. The constructor builds
|
||||
the skeleton (sizers, and the parts other code may touch) and queues the rest with
|
||||
`add_build_step()`. `build_step()` runs one step, and `add_build_steps_of(child)`
|
||||
forwards a child's steps so a nested panel is spread the same way; the parent is built
|
||||
only once the child is, including steps the child queues later. Steps run in order, on
|
||||
the main thread. A `Lazy<T>` recognises a staged type at compile time and runs its steps
|
||||
one per unit.
|
||||
|
||||
Nothing may touch what a step builds before the last step has run. In practice:
|
||||
|
||||
- nothing paints the panel before it is complete, since a panel built at idle is hidden
|
||||
with its page and one built on demand finishes inside `ensure()` before the event loop
|
||||
runs again;
|
||||
- members created in steps are initialised to null in the header, so a partially built
|
||||
panel can be destroyed;
|
||||
- timers and event handlers that use step content check `built()` first
|
||||
(`MonitorPanel::update_all()`, `CalibrationPanel::update_all()`), and a child's steps
|
||||
are queued before anything in the constructor can fire such a handler;
|
||||
- a destructor that disconnects from step content checks `built()` first;
|
||||
- a constructor or step does not take focus while the panel is off screen
|
||||
(`IsShownOnScreen()` before `SetFocus()`), since it may run while the user is typing
|
||||
- members created in steps start out null, so a partly built panel can be destroyed;
|
||||
- timers, event handlers and destructors that touch step content check that the panel is
|
||||
complete first;
|
||||
- nothing takes focus while off screen, since a unit may run while the user is typing
|
||||
elsewhere;
|
||||
- where a step's widgets must keep their place in a sizer that later steps also fill, the
|
||||
constructor adds an empty slot sizer in that position and the step fills the slot
|
||||
(`StatusBasePanel`).
|
||||
- a widget added by a step keeps its place in the sizer through an empty slot the skeleton
|
||||
creates.
|
||||
|
||||
### IdleScheduler: when to build
|
||||
### The scheduler: `IdleScheduler` and `PrebuildQueue`
|
||||
|
||||
A task is any `LazyBase`: a name for the log, `pending()`, `build_step()` and
|
||||
`prebuild_order()`. `PrebuildQueue` holds the tasks by order (equal order in the order
|
||||
added) and runs a slice, the units of the first pending task until it completes, the
|
||||
budget is spent on the clock it is given, or the interrupt predicate says input arrived.
|
||||
It has no wx dependency and is unit-tested with a fake clock. A task whose work is gone
|
||||
is passed over and stays in the queue, so a tab that `show_device()` removes and later
|
||||
re-inserts is pending again.
|
||||
The queue holds tasks in order, and a slice runs units of the first pending task until
|
||||
the task finishes, the time budget is spent, or input arrives. It has no wx dependency and
|
||||
is unit-tested with a fake clock. A task whose work goes away, such as a tab removed from
|
||||
the book, is skipped, and becomes pending again if the work comes back.
|
||||
|
||||
`IdleScheduler` drives the queue with the real clock, the app's input timestamp and the
|
||||
Windows queue check, and logs each unit and slice. Each slice is a timer message: a
|
||||
period of 250 ms while waiting for the user to go quiet, and a one-shot of zero after a
|
||||
slice that left work, so the event loop dispatches whatever it has queued (paint,
|
||||
timers, input) before the next slice runs. Chaining slices with `CallAfter` would not do
|
||||
this: wx drains pending events fully before the next native message, on every platform.
|
||||
On GTK the one-shot is 5 ms, because a due GLib timeout runs ahead of the redraw and
|
||||
idle sources that paint and deliver posted events.
|
||||
A slice runs only once the user has been idle for the quiet time, and the timer stops
|
||||
itself once no task is pending. The tick period, quiet time and slice length are
|
||||
constants in `IdleScheduler.cpp`. A unit cannot be interrupted once started, so the
|
||||
largest unit bounds click latency on Windows; on the other platforms a click also waits
|
||||
for the rest of the slice. A unit that pumps the event loop lets the timer fire inside
|
||||
its own slice, and that tick does nothing.
|
||||
A slice runs only once the user has been idle for a short quiet time, and each slice is its
|
||||
own timer message, so paint, timers and input queued in between are handled before the
|
||||
next slice. Posting slices as pending events would not do that, because wx drains every
|
||||
pending event before the next native message. On GTK a timer that is always due starves
|
||||
the lower-priority sources that repaint and deliver posted events, so slices are a few
|
||||
milliseconds apart. On Windows a slice also waits while the native queue holds input,
|
||||
not counting mouse moves, which Windows synthesizes whenever a window appears under the
|
||||
cursor. A slice never runs inside a `wxYield()`, where it would build pages in the middle of
|
||||
the code that yielded. A unit cannot be interrupted, so the largest unit bounds how long a
|
||||
click can wait.
|
||||
When nothing is pending the timer stops and the subsystem costs nothing.
|
||||
|
||||
The tasks are made by their owners. `MainFrame::prebuild_pages_when_idle()` registers
|
||||
every `LazyPage` the frame created, in or out of the book (`pending()` is false for a
|
||||
page out of the book), the Compare presets holder, and the Prepare sidebar's settings
|
||||
page from `ParamsPanel::settings_page_prebuild()`, whose first unit selects the default
|
||||
tab if none is selected yet and whose later units build one option group each.
|
||||
`show_device()` only restarts the timer. `MainFrame` owns the scheduler because it owns
|
||||
everything the tasks build, and clearing the queue with the frame is what keeps a task
|
||||
from outliving its object.
|
||||
The main window owns the scheduler because it owns what the tasks build, and clearing the
|
||||
queue with the window keeps a task from outliving its object. Each owner provides its own
|
||||
tasks, such as a tab, a dialog, the Prepare tab's settings page one option group at a
|
||||
time, the Prepare page's layout at the size the book gives its pages, or the 3D view's GL
|
||||
resources.
|
||||
|
||||
Idle time comes from `GUI_App::FilterEvent`, which timestamps mouse and keyboard events
|
||||
(`wxEVT_CATEGORY_USER_INPUT` minus command events, which all claim that category), and
|
||||
`GUI_App::input_idle_ms()` reports it. On Windows a slice additionally refuses to start
|
||||
when the message queue holds keyboard, button, touch or pen input. Mouse moves are
|
||||
excluded because Windows synthesises one whenever a window appears under the cursor,
|
||||
which every unit does. Other platforms use the timestamp alone.
|
||||
### The 3D view's GL resources
|
||||
|
||||
Once every registered page is built the timer is stopped and the subsystem costs
|
||||
nothing.
|
||||
OpenGL is loaded on the Prepare tab's canvas. When the start page is not Prepare, loading
|
||||
it is an idle task that makes the context current on the hidden canvas, so the start page
|
||||
paints first and Prepare never appears. Loading it on a shown canvas under `Freeze()` holds
|
||||
back the start page's paint, and on GTK `Freeze()` cannot hide the canvas, which is a
|
||||
native child window or a Wayland subsurface drawn outside GTK. A hidden Windows child
|
||||
window keeps its device context, macOS attaches the context to a hidden view, and GTK
|
||||
creates the canvas's surface when the widget is realized, so on GTK the task realizes the
|
||||
canvas first. If the context cannot be made current, the canvas's first render loads the
|
||||
resources.
|
||||
|
||||
## Rules
|
||||
|
||||
**What builds before the first frame.** The Prepare tab's plater, because `post_init()`
|
||||
needs its GL canvas on screen to initialise OpenGL in every startup state, and the start
|
||||
page the user configured. Home is built by the first `MainFrame::Show()`, Prepare's
|
||||
settings page by selecting the tab. `post_init()` passes through the Prepare tab for GL
|
||||
init under `Freeze()` with `MainFrame::select_prepare_for_gl_init()`, which changes the
|
||||
selection without the page-changed event, so nothing else is built for that pass.
|
||||
**Before the first frame.** Only the start page and the Prepare tab's plater are built
|
||||
before the first frame. Everything else goes through a holder.
|
||||
|
||||
**Reaching a lazy object.** A caller uses the type's own statics. `T::if_built()` may
|
||||
return null and is for telling the object something it can live without (a rescale, a
|
||||
colour change, a status update). `T::ensure()` builds the object and is for navigating
|
||||
to it or for a caller that is about to show it. A caller that tells the object something
|
||||
it would not fetch for itself on construction uses `T::when_built()`, which keeps the
|
||||
message until the object exists. A panel that pulls its state when constructed (the Home
|
||||
page requests the recent list on load, the Device tab reads the device manager on show)
|
||||
is reached with `if_built()`; one that cannot pull gets `when_built()`.
|
||||
**Reaching a lazy object.** Callers use the type's statics. Reading it if built is for
|
||||
things the object can live without, such as a rescale, a color change or a status refresh.
|
||||
Making sure it exists is for navigating to it or showing it. Running something once it is
|
||||
built is for state it would not fetch for itself on construction. A panel that pulls its
|
||||
state when constructed only ever needs to be read if built.
|
||||
|
||||
**Unit size.** A unit cannot be interrupted, so it should stay within the slice length on
|
||||
a fast machine. A constructor above that is staged. A single widget above it is the
|
||||
floor unless the widget itself is split.
|
||||
**Unit size.** A unit should fit in one slice on a fast machine. A constructor over that is
|
||||
staged, and a single widget over it is accepted unless the widget itself can be split.
|
||||
|
||||
**Order.** Cheapest and most likely to be opened first, given where `MainFrame` creates
|
||||
each holder. The settings page is 0 (the Prepare tab's own content), Home 10, Device 20
|
||||
(a Bambu user's usual second stop), Calibration 30, Multi-device 40, the web Device
|
||||
view 50, Project 60 (a second WebView2 instance) and the Compare presets dialog 100;
|
||||
steps of ten so a new tab takes a value between its neighbours without renumbering
|
||||
them. `MainFrame::prebuild_pages_when_idle()` registers the tasks once, from
|
||||
`post_init()`.
|
||||
|
||||
**Never prebuilt.** A holder with a negative order, for a tab that few sessions open
|
||||
and that costs more to build unasked than it saves (the Design tab), and plugin-provided
|
||||
tabs, which are Python-side and not lazy pages.
|
||||
**Order.** Tasks run cheapest and most likely to be opened first. Each holder's order is
|
||||
given where it is created, with gaps so a new task fits between its neighbors. A negative
|
||||
order is never prebuilt, for something few sessions open that costs more to build unasked
|
||||
than it saves.
|
||||
|
||||
## Adopting it
|
||||
|
||||
A lazy tab needs:
|
||||
A lazy tab needs a panel type deriving from `LazyInstance`, a placeholder page the main
|
||||
window creates once with its order and registers for the idle build, and every use of the
|
||||
panel outside the main window going through the statics. Its constructor has to cope with
|
||||
the main window already existing and the user being busy elsewhere, so it takes no focus
|
||||
while off screen, and it does all of its own setup, since the main window does nothing to a
|
||||
panel after creating it.
|
||||
|
||||
1. The panel derived from `LazyInstance<Panel>`, since a tab's panel has one instance.
|
||||
2. A `LazyPage<Panel>*` member in `MainFrame`, created once in `init_tabpanel()` with
|
||||
its `TAB_ID_*` as the name, its place by the order rule (and a factory if
|
||||
`new Panel(parent)` is not enough), added to `m_lazy_pages`, and used wherever the
|
||||
tab is added to or looked up in the notebook.
|
||||
3. Every use of the panel outside `MainFrame` going through one of the panel's statics,
|
||||
chosen by the rule above. When converting an existing member, `grep` for it; the
|
||||
compiler finds the rest.
|
||||
4. A constructor that copes with the frame already existing and the user being busy
|
||||
elsewhere, since `wxGetApp().mainframe` is set, the frame may be shown, and the user
|
||||
may be typing when a lazy panel is built: no `SetFocus()` while off screen, and
|
||||
whatever the constructor did through a `MainFrame` accessor before (a mode update, a
|
||||
deferred URL) done in the constructor itself.
|
||||
To stage a heavy constructor, keep the skeleton in the constructor, move the rest into
|
||||
steps in its original order, and follow the staged-construction constraints. Measure the
|
||||
units; a step that is still one big widget is split inside the widget or accepted.
|
||||
|
||||
To stage a heavy constructor, inherit `StagedBuild`, keep the skeleton in the constructor,
|
||||
move the rest into `add_build_step()` lambdas in the original order, and follow the
|
||||
staged-panel rules above. Measure the units. A step that is still one big widget has to
|
||||
be split inside that widget or accepted as the floor.
|
||||
## Verifying
|
||||
|
||||
Verification is by log. `MainFrame::prebuild_pages_when_idle` lists the queue it
|
||||
registered, `IdleScheduler::tick` reports each completed task by name at info level and
|
||||
each slice and unit at debug level, and `Lazy::ensure` reports an object a user built
|
||||
on demand with the units and time it took. A run from the configured start
|
||||
page shows every registered page complete, in order, with no unit longer than intended.
|
||||
A click on a tab mid-prebuild shows the finished panel with an `ensure` line for what was
|
||||
left, and the slices resume for the remaining tasks once the user is idle again.
|
||||
The log lists the queue when it is registered, reports each completed task at info level
|
||||
and each slice and unit at debug level, and reports every on-demand build with its units
|
||||
and time. A task's completion line counts only the slice it finished in. A run from the
|
||||
configured start page should show every registered task complete in order, with no unit
|
||||
longer than intended. A click on a tab during the idle build should show an on-demand
|
||||
build for what was left, with the slices resuming once the user is idle again.
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# Multiline infill — High Level Design
|
||||
|
||||
## Purpose and scope
|
||||
|
||||
`fill_multiline` prints every sparse infill wall as N adjacent lines instead of
|
||||
one, so a wall is `d1 = N * spacing` thick. Only internal sparse infill uses it.
|
||||
Each pattern first builds its single-line centerlines at N times the usual line
|
||||
spacing (so the density holds), and `multiline_fill()` then replaces each
|
||||
centerline by the lines of that wall: the centerline itself when N is odd, and
|
||||
closed outlines around it at every `spacing` out to `d1 / 2`. The outlines are
|
||||
clipped to the fill region contracted by half a line width, then connected like
|
||||
any other infill.
|
||||
|
||||
Outlines of centerlines that cross each other overlap at every crossing, which
|
||||
over-extrudes the wall intersections. The line-crossing patterns Grid,
|
||||
Triangles, Tri-hexagon and Cubic therefore build centerlines that never cross
|
||||
(`FillRectilinear::fill_surface_trapezoidal()`), and so do Adaptive Cubic and
|
||||
Support Cubic (`FillAdaptive`); the other patterns outline their usual
|
||||
centerlines.
|
||||
|
||||
## Non-crossing centerlines
|
||||
|
||||
The crossing lines are resolved into x-monotone paths, the levels of the line
|
||||
arrangement: walking along x, the k-th path is always the k-th line from the
|
||||
bottom. At every crossing, the two paths bounce off each other instead of
|
||||
passing through. Adjacent paths meet only at crossings, so their outlines touch
|
||||
there and nowhere overlap.
|
||||
|
||||
Where two paths meet, each is cut short by a line perpendicular to the bisector
|
||||
of its bend, `d1 / 2` from the crossing. The two cut segments are parallel and
|
||||
`d1` apart, so the outermost lines of the two walls sit exactly `spacing` apart,
|
||||
like the lines inside a wall. Where three lines meet at one point, the middle
|
||||
path runs straight through and the outer two are cut `d1` from it.
|
||||
|
||||
Each pattern builds its rows along x in a rotated frame. Grid lines run at ±45°
|
||||
there, and its rows are trapezoid waves that transpose on alternate layers. The three families of Triangles, Tri-hexagon and
|
||||
Cubic run at 0°, 60° and 120°. Those rows rotate by 120° every layer about a
|
||||
3-fold center of the arrangement, so each family takes every role in turn.
|
||||
The pattern is phased on fixed positions, so it lines up across layers and
|
||||
across the regions of one layer. Rounding the corners with
|
||||
`sparse_infill_smooth_factor` happens before `multiline_fill()`.
|
||||
|
||||
## Cubic
|
||||
|
||||
Single-line Cubic draws the three families at the same spacing `h` and shifts
|
||||
them with z: by `+dx`, `-dx` and `+dx`, `dx = z / sqrt(2)`. The multiline paths
|
||||
follow the same lines. In the frame where one family is horizontal, the other two
|
||||
cross in rows `h` apart, alternating by half a period, at height
|
||||
`tau = -3 * dx (mod h)` above the horizontal line below them. The crossings split
|
||||
every band between horizontal lines into up-pointing triangles of height `tau`,
|
||||
down-pointing triangles of height `h - tau`, and hexagons. At `tau = 0` (and `h`)
|
||||
all three families meet at common points, as in Triangles. At `tau = h / 2` the
|
||||
triangles are equal, as in Tri-hexagon. The origin of that frame is always a
|
||||
3-fold center, whatever z is, so the per-layer rotation keeps the lines in place.
|
||||
|
||||
Each band holds two paths that touch at its crossings: the upper one takes the
|
||||
V below the crossing and runs along the top horizontal line, and the lower one
|
||||
takes the inverted V above it and runs along the bottom line. Both are the same function
|
||||
of `tau`, the lower one mirrored with `h - tau`. `cubic_upper_level()` builds one
|
||||
period of the upper path as the lower envelope of five lines, clipped from below:
|
||||
|
||||
- the two slanted lines through the crossings,
|
||||
- the horizontal line, lowered when the triangle above it is less than `1.5 * d1` high,
|
||||
- the two chamfers where the path turns onto and off the horizontal line, `d1 / 2`
|
||||
from those crossings,
|
||||
- the flat cut into the V at the crossing.
|
||||
|
||||
The cut height `clamp(tau - d1 / 2, 0, h - d1) + d1` is what makes the pattern
|
||||
continuous in z. While both triangles are at least `1.5 * d1` high, every
|
||||
crossing is a pair of bends `d1 / 2` from it, as in Tri-hexagon. When a triangle
|
||||
is thinner, its three paths stack like a triple crossing. The path through it
|
||||
flattens toward its base line and lies on it once the triangle is under `d1 / 2`
|
||||
high, and the paths beside it are pushed `d1` away. The layout thus reaches the
|
||||
Triangles one where the families meet. Adjacent paths stay at least `d1` apart
|
||||
at every `tau` and at every density up to 100%.
|
||||
|
||||
## Adaptive Cubic
|
||||
|
||||
Adaptive Cubic and Support Cubic take their lines from an octree of cubes
|
||||
standing on a corner. On each layer every cube cuts its three mid-planes into
|
||||
segments of the same three 60° families as Cubic, but the pattern is not
|
||||
periodic. Smaller cubes near the surface add finer lines, and a finer line ends
|
||||
where it meets the wall of its coarser cube, so the lines form crossings and
|
||||
T-junctions. `FillAdaptive::multiline_paths()` builds the paths from these
|
||||
segments directly, for each fill region and within `4 * d1` of it.
|
||||
|
||||
At a crossing the two paths bounce as in Cubic. At a T-junction the through line
|
||||
runs straight on and the path of the ending line stops there. Every path still
|
||||
runs left to right in the frame where one family is horizontal, and that family
|
||||
rotates with the layer.
|
||||
|
||||
Every line of every cube size lies on one fine lattice, so crossings closer than
|
||||
a few `d1` are the corners of one small triangle of that lattice, as in Cubic.
|
||||
The cuts follow the Cubic rules without a closed formula:
|
||||
|
||||
- The two bends of a crossing are cut `d1` apart, `d1 / 2` each, perpendicular
|
||||
to their bisector, so their walls touch. A cut goes no further than the path
|
||||
end, and the other bend takes the rest of `d1`.
|
||||
- At the tip of a small triangle, between the two slanted families, a cut also
|
||||
goes no further than the neighbouring bend turning the other way, and the
|
||||
path beyond that bend is kept a wall away from it. The bends onto the
|
||||
horizontal family are not limited this way: pushing their paths apart would
|
||||
open gaps between walls that should touch.
|
||||
- A cut moves the path only where the cut line lies beyond it, near its bend.
|
||||
The sharp bends between the two slanted families are cut after the bends onto
|
||||
the horizontal family, so the tip of a small triangle wins, as in Cubic.
|
||||
- A path stopping at a T-junction is trimmed until it is `d1` less half a line
|
||||
spacing from every other path, so that its end overlaps the wall it stops on
|
||||
by half a line and bonds to it. The paths are trimmed one at a time against
|
||||
the others as already trimmed, so two ends facing each other meet instead of
|
||||
both backing off. A path stopping on the line of another is trimmed before
|
||||
that one, so it gives way and the other still reaches the line it stops on. A
|
||||
second round trims every path again from its full length, so an end grows
|
||||
back where the ends it gave way to were trimmed later, and a last round only
|
||||
shortens them, keeping them that far apart. Paths shorter than `d1` are left
|
||||
out.
|
||||
- A line that ends on another less than `2 * d1` past a crossing stops at that
|
||||
crossing instead, the shorter one where both do. The path along such a stub
|
||||
would be trimmed away, leaving a hole between the walls that were cut to
|
||||
touch it.
|
||||
|
||||
Short paths enclosed by coarser lines still print as closed outlines, but most
|
||||
paths run on across several cells.
|
||||
@@ -0,0 +1,197 @@
|
||||
# Prime tower sparse layers — High Level Design
|
||||
|
||||
## Purpose and scope
|
||||
|
||||
A prime tower exists to absorb filament changes, but it is planned on every
|
||||
object layer below the topmost change, not only on the layers that purge. The
|
||||
layers in between carry no filament change and print nothing but a block of the
|
||||
tower's own footprint to keep its top level. They are called sparse layers, and
|
||||
on a print with few changes they are most of the tower: they cost time, filament
|
||||
and a travel to the tower on every layer.
|
||||
|
||||
Two settings trade that cost against something else. `wipe_tower_no_sparse_layers`
|
||||
drops them, which sinks the tower below the model. `wipe_tower_sparse_layers_combination`
|
||||
merges runs of them into fewer, thicker layers, which keeps the tower level with
|
||||
the model. Both are off by default, and with both off the tower prints one layer
|
||||
per object layer as it always has.
|
||||
|
||||
The decisions belong to tower planning and G-code emission. They do not change
|
||||
sliced object geometry, but they do change the emitted G-code, the filament and
|
||||
time estimates, and — for the compacted case — whether a plate is printable at
|
||||
all. Changing either setting invalidates the tower step.
|
||||
|
||||
## What a sparse layer is
|
||||
|
||||
`ToolOrdering::fill_wipe_tower_partitions` counts the filament changes per layer
|
||||
and propagates that count downwards, so every layer below the topmost change is
|
||||
marked as carrying a tower. It then fills any gap between two tower layers, so
|
||||
the tower is continuous from the bed to its last purge. `wipe_tower_layer_height`
|
||||
is the distance from the previous tower layer, which is the object's layer height
|
||||
whenever the tower prints on every layer.
|
||||
|
||||
`Print::_make_wipe_tower` plans one tower layer per such object layer. A layer
|
||||
whose only call keeps the current filament leaves no toolchange in the plan, and
|
||||
the layer it generates is a single result whose initial and new tool are equal.
|
||||
That is what `wipe_tower_layer_is_sparse` recognises, and it is the unit both
|
||||
settings work on.
|
||||
|
||||
The plan stays one entry per tower layer in every case. The G-code emitter walks
|
||||
`WipeTowerData::tool_changes` by layer index, advancing once per object layer
|
||||
that carries a tower, so a planner that removed entries would silently shift
|
||||
every later layer onto the wrong tower geometry. Layers that print nothing are
|
||||
therefore still planned and still generated; they are marked, and the emitter
|
||||
drops them.
|
||||
|
||||
## Shared rules
|
||||
|
||||
Tower planning, G-code emission and the plate validation all have to agree about
|
||||
which layers print and where. They ask one set of free functions, declared beside
|
||||
the tower classes, rather than each re-deriving the answer from the raw options:
|
||||
|
||||
- `wipe_tower_sparse_layers_skipped` — whether sparse layers are really dropped.
|
||||
Smooth timelapse and clumping detection park the nozzle on the tower every
|
||||
layer, so with either of them on no layer is ever dropped and the option reads
|
||||
as off everywhere.
|
||||
- `wipe_tower_sparse_layers_combined` — whether runs are really merged. The same
|
||||
two rule it out, and so does `wipe_tower_no_sparse_layers`: dropping the layers
|
||||
outright is the stronger answer to the same problem, so the two settings are
|
||||
exclusive and the GUI greys out the second while the first is on.
|
||||
- `wipe_tower_layer_is_sparse`, `wipe_tower_layer_is_combined_away` — per-layer
|
||||
questions the emitter asks about generated results.
|
||||
- `compute_compacted_wipe_tower_z` — the tower's print z per planned layer when
|
||||
it is compacted.
|
||||
- `combine_sparse_wipe_tower_layers` and its `combine_sparse_wipe_tower_plan`
|
||||
wrapper — the merge rule, applied to either generator's plan.
|
||||
|
||||
Both tower generators are driven through these. `WipeTower` (Type 1, the block
|
||||
tower) and `WipeTower2` (Type 2, the default) keep separate plans with the same
|
||||
per-layer shape — print z, layer height, toolchanges, and a `combined_away` flag
|
||||
— so one template covers both.
|
||||
|
||||
## Dropping sparse layers
|
||||
|
||||
With `wipe_tower_no_sparse_layers`, the tower only grows on layers that carry a
|
||||
real change. It therefore falls one layer height behind the object for every
|
||||
sparse layer, and by the top of a tall print it can sit far below the model. The
|
||||
nozzle has to reach down to it at each purge.
|
||||
|
||||
`compute_compacted_wipe_tower_z` derives that z once, from the generated results,
|
||||
so the emitter and the validator cannot disagree. Emission descends to it, but
|
||||
only once the nozzle is parked over the tower: descending while still over the
|
||||
model would drive the nozzle into the print, so a descent that would do that is
|
||||
deferred until after the travel to the tower. Extrusions emitted without an
|
||||
explicit z — the nozzle-change wipe in particular — are pulled down to the
|
||||
compacted z for the same reason.
|
||||
|
||||
Reaching down is only safe if nothing tall stands near the tower. `Print.hpp`
|
||||
carries the clearance rule: a keep-out zone grown from the tower's footprint by
|
||||
the spiral z-hop envelope, and a per-object limit on how high an object may rise
|
||||
near it, tiered by the nozzle cone, the head body, the rod and the lid. The same
|
||||
rule serves the precise check on real extrusions, the pre-slice estimate that
|
||||
feeds the plater, and the outlines the plater draws while an object is dragged,
|
||||
so that the ring the user sees touches the object's outline exactly when the
|
||||
check trips.
|
||||
|
||||
## Merging sparse layers
|
||||
|
||||
With `wipe_tower_sparse_layers_combination`, no layer is dropped and nothing is
|
||||
compacted: the tower keeps following the object, and the nozzle never descends.
|
||||
Instead a run of consecutive sparse layers prints once, on the run's last layer,
|
||||
at the accumulated height of everything it covers — the same way infill
|
||||
combination merges sparse infill. The layers below it in the run print nothing.
|
||||
|
||||
`combine_sparse_wipe_tower_plan` runs before the tower's depths are planned,
|
||||
because the heights it rewrites feed the extrusion flow of every later pass. It
|
||||
raises `height` in place on the layer that prints a run and sets `combined_away`
|
||||
on the rest; generation then proceeds unchanged, and the flag is copied onto the
|
||||
results so the emitter can drop them.
|
||||
|
||||
Four constraints shape the rule:
|
||||
|
||||
- **Whole layers only.** A tower layer is entered at the object's z, so a merged
|
||||
layer has to end on an object layer boundary. The merged height is therefore a
|
||||
sum of whole layer heights, never a clamped value.
|
||||
- **The nozzle's maximum layer height.** A run stops growing as soon as one more
|
||||
layer would pass `max_layer_height` for the nozzle printing it — three quarters
|
||||
of the nozzle diameter when that is left at 0, as elsewhere in slicing. The cap
|
||||
is read through the filament-to-nozzle map, since `max_layer_height` is per
|
||||
nozzle while the tower indexes filaments. This is what makes the setting inert
|
||||
at common layer heights: two 0.2 mm layers are 0.4 mm and do not fit under a
|
||||
0.3 mm maximum, so nothing merges until the layer height is 0.15 mm or below,
|
||||
or the maximum is raised.
|
||||
- **A filament change purges at its own z.** A layer with a real change can
|
||||
neither be merged away nor absorb the run below it, so a run always ends on its
|
||||
own last sparse layer and the change above it is untouched.
|
||||
- **The first layer stays on the bed.** It carries the brim and is never merged.
|
||||
|
||||
A run holds one filament throughout — that is what makes it sparse — so the cap
|
||||
is uniform across it, and the tower reserves depth only for the purges above a
|
||||
layer, so a run has one footprint and the merged layer covers exactly the area
|
||||
the layers it replaces would have.
|
||||
|
||||
## Emission and accounting
|
||||
|
||||
`WipeTowerIntegration` drops a layer whose results are marked, for both settings,
|
||||
through the same `ignore_sparse` path in `tool_change` and
|
||||
`is_empty_wipe_tower_gcode`. A dropped layer emits no travel to the tower and no
|
||||
extrusion.
|
||||
|
||||
Filament used is accumulated by the generators while they write, so a layer that
|
||||
will be dropped must not be charged. Type 1 asks `layer_is_printed` at each of
|
||||
its accumulation points; Type 2 guards the equivalent block in `finish_layer`,
|
||||
which also stops a merged-away layer from adding height of its own — the layer
|
||||
that prints the run carries all of it.
|
||||
|
||||
A merged layer is the only case where the tower's layer height differs from the
|
||||
object layer it sits on, and therefore the only case where the height the
|
||||
exporter already emitted for that layer is wrong for the tower. Both generators
|
||||
do declare a height, but each hardcodes a tag dialect — the block tower forces
|
||||
the BBL tag, the other writes the compatible one — while the G-code processor
|
||||
reads only the tag its printer uses. On a non-BBL printer with a Type 1 tower the
|
||||
declaration is dropped, and the merged layer is drawn and costed as a thin one.
|
||||
`WipeTowerIntegration::tower_height_tag` therefore declares it at export time,
|
||||
where the printer is known, and only when the tower's own G-code does not already
|
||||
carry the tag that will be read. The object's height returns on the next object
|
||||
path, because emission forces the processor role to the tower on any layer that
|
||||
carries one.
|
||||
|
||||
## Constraints
|
||||
|
||||
A layer that prints nothing prints nothing at all, including any interface work
|
||||
the tower planner scheduled there. The Type 1 block planner marks a layer as a
|
||||
contact layer when a filament category stops or starts being used relative to the
|
||||
layer below, and a sparse layer immediately above a change qualifies. Merging a
|
||||
run, like dropping its layers, replaces that interface with the run's single
|
||||
layer. Both settings are off by default for this among other reasons.
|
||||
|
||||
Neither setting changes what the tower is for. A plate that needs a tower on
|
||||
every layer — smooth timelapse, clumping detection — gets one, and the settings
|
||||
read as off rather than compacting or merging in one place and not another.
|
||||
|
||||
## Implementation and verification
|
||||
|
||||
- [WipeTower.hpp](../../src/libslic3r/GCode/WipeTower.hpp) declares the shared
|
||||
rules and the plan-merging template;
|
||||
[WipeTower.cpp](../../src/libslic3r/GCode/WipeTower.cpp) implements them and
|
||||
the Type 1 tower, [WipeTower2.cpp](../../src/libslic3r/GCode/WipeTower2.cpp)
|
||||
the Type 2 tower.
|
||||
- [ToolOrdering.cpp](../../src/libslic3r/GCode/ToolOrdering.cpp) decides which
|
||||
layers carry a tower at all, and
|
||||
[Print.cpp](../../src/libslic3r/Print.cpp) plans it and runs the clearance
|
||||
check whose rule lives in [Print.hpp](../../src/libslic3r/Print.hpp).
|
||||
- [GCode.cpp](../../src/libslic3r/GCode.cpp) emits the tower, drops the layers
|
||||
that print nothing, and declares a merged layer's height;
|
||||
[PrintConfig.cpp](../../src/libslic3r/PrintConfig.cpp) defines the settings and
|
||||
[ConfigManipulation.cpp](../../src/slic3r/GUI/ConfigManipulation.cpp) their
|
||||
mutual exclusion.
|
||||
- [GLCanvas3D.cpp](../../src/slic3r/GUI/GLCanvas3D.cpp) and
|
||||
[PartPlate.cpp](../../src/slic3r/GUI/PartPlate.cpp) draw the compacted tower's
|
||||
keep-out outlines live while the user drags.
|
||||
- [Rule tests](../../tests/libslic3r/test_wipe_tower.cpp) cover the gating of
|
||||
both settings, the per-layer predicates, the compacted z, the merge rule's run
|
||||
flushing, height conservation, the nozzle cap and the first-layer exemption,
|
||||
and the clearance geometry the plater draws.
|
||||
- [Slicing tests](../../tests/fff_print/test_wipe_tower.cpp) slice a real print
|
||||
and check that a run folds, that the tower still covers the object exactly
|
||||
once, that a run too thin for the cap is left alone, and that a merged layer
|
||||
declares its height in the tag the printer's processor reads.
|
||||
+155
-55
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Blocks",
|
||||
"version": "02.04.00.04",
|
||||
"version": "02.04.00.13",
|
||||
"force_update": "0",
|
||||
"description": "Blocks configurations",
|
||||
"machine_model_list": [
|
||||
@@ -43,8 +43,8 @@
|
||||
"sub_path": "process/0.16mm Optimal 0.4 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard 0.4 nozzle @Blocks",
|
||||
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks.json"
|
||||
"name": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard 0.4 nozzle @Blocks_RD50_V2",
|
||||
@@ -55,8 +55,8 @@
|
||||
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.24mm Draft 0.4 nozzle @Blocks",
|
||||
"sub_path": "process/0.24mm Draft 0.4 nozzle @Blocks.json"
|
||||
"name": "0.24mm Draft 0.4 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.24mm Draft 0.4 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.24mm Draft 0.4 nozzle @Blocks_RD50_V2",
|
||||
@@ -75,8 +75,8 @@
|
||||
"sub_path": "process/0.28mm Extra Draft 0.4 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks",
|
||||
"sub_path": "process/0.30mm Extra Draft 0.4 nozzle @Blocks.json"
|
||||
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_process_common 0.6 nozzle",
|
||||
@@ -111,8 +111,8 @@
|
||||
"sub_path": "process/0.26mm Standard 0.6 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.30mm Standard 0.6 nozzle @Blocks",
|
||||
"sub_path": "process/0.30mm Standard 0.6 nozzle @Blocks.json"
|
||||
"name": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.30mm Standard 0.6 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.32mm Draft 0.6 nozzle @Blocks_RD50_V2",
|
||||
@@ -131,12 +131,12 @@
|
||||
"sub_path": "process/0.38mm Extra Draft 0.6 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.40mm Draft 0.6 nozzle @Blocks",
|
||||
"sub_path": "process/0.40mm Draft 0.6 nozzle @Blocks.json"
|
||||
"name": "0.40mm Draft 0.6 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.40mm Draft 0.6 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.30mm Optimal 0.8 nozzle @Blocks",
|
||||
"sub_path": "process/0.30mm Optimal 0.8 nozzle @Blocks.json"
|
||||
"name": "0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.30mm Optimal 0.8 nozzle @Blocks_RD50_V2",
|
||||
@@ -155,8 +155,8 @@
|
||||
"sub_path": "process/0.38mm Standard 0.8 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.40mm Standard 0.8 nozzle @Blocks",
|
||||
"sub_path": "process/0.40mm Standard 0.8 nozzle @Blocks.json"
|
||||
"name": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.40mm Standard 0.8 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.46mm Draft 0.8 nozzle @Blocks_RD50_V2",
|
||||
@@ -167,8 +167,8 @@
|
||||
"sub_path": "process/0.46mm Draft 0.8 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.50mm Draft 0.8 nozzle @Blocks",
|
||||
"sub_path": "process/0.50mm Draft 0.8 nozzle @Blocks.json"
|
||||
"name": "0.50mm Draft 0.8 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.50mm Draft 0.8 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.54mm Extra Draft 0.8 nozzle @Blocks_RD50_V2",
|
||||
@@ -179,36 +179,36 @@
|
||||
"sub_path": "process/0.54mm Extra Draft 0.8 nozzle @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "0.30mm Optimal 1.0 nozzle @Blocks",
|
||||
"sub_path": "process/0.30mm Optimal 1.0 nozzle @Blocks.json"
|
||||
"name": "0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.50mm Standard 1.0 nozzle @Blocks",
|
||||
"sub_path": "process/0.50mm Standard 1.0 nozzle @Blocks.json"
|
||||
"name": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.50mm Standard 1.0 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.60mm Draft 1.0 nozzle @Blocks",
|
||||
"sub_path": "process/0.60mm Draft 1.0 nozzle @Blocks.json"
|
||||
"name": "0.60mm Draft 1.0 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.60mm Draft 1.0 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks",
|
||||
"sub_path": "process/0.70mm Extra Draft 1.0 nozzle @Blocks.json"
|
||||
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.50mm Optimal 1.2 nozzle @Blocks",
|
||||
"sub_path": "process/0.50mm Optimal 1.2 nozzle @Blocks.json"
|
||||
"name": "0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.60mm Standard 1.2 nozzle @Blocks",
|
||||
"sub_path": "process/0.60mm Standard 1.2 nozzle @Blocks.json"
|
||||
"name": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.60mm Standard 1.2 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.70mm Draft 1.2 nozzle @Blocks",
|
||||
"sub_path": "process/0.70mm Draft 1.2 nozzle @Blocks.json"
|
||||
"name": "0.70mm Draft 1.2 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.70mm Draft 1.2 nozzle @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks",
|
||||
"sub_path": "process/0.80mm Extra Draft 1.2 nozzle @Blocks.json"
|
||||
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100",
|
||||
"sub_path": "process/0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100.json"
|
||||
}
|
||||
],
|
||||
"filament_list": [
|
||||
@@ -249,48 +249,148 @@
|
||||
"sub_path": "filament/fdm_filament_tpu.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic ABS @Blocks",
|
||||
"sub_path": "filament/Generic ABS @Blocks.json"
|
||||
"name": "Blocks ABS @base",
|
||||
"sub_path": "filament/Blocks ABS @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic ASA @Blocks",
|
||||
"sub_path": "filament/Generic ASA @Blocks.json"
|
||||
"name": "Blocks ASA @base",
|
||||
"sub_path": "filament/Blocks ASA @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic ASA-CF @Blocks",
|
||||
"sub_path": "filament/Generic ASA-CF @Blocks.json"
|
||||
"name": "Blocks ASA-CF @base",
|
||||
"sub_path": "filament/Blocks ASA-CF @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PA @Blocks",
|
||||
"sub_path": "filament/Generic PA @Blocks.json"
|
||||
"name": "Blocks PA @base",
|
||||
"sub_path": "filament/Blocks PA @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PA-CF @Blocks",
|
||||
"sub_path": "filament/Generic PA-CF @Blocks.json"
|
||||
"name": "Blocks PA-CF @base",
|
||||
"sub_path": "filament/Blocks PA-CF @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PC @Blocks",
|
||||
"sub_path": "filament/Generic PC @Blocks.json"
|
||||
"name": "Blocks PC @base",
|
||||
"sub_path": "filament/Blocks PC @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PETG @Blocks",
|
||||
"sub_path": "filament/Generic PETG @Blocks.json"
|
||||
"name": "Blocks PETG @base",
|
||||
"sub_path": "filament/Blocks PETG @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PLA @Blocks",
|
||||
"sub_path": "filament/Generic PLA @Blocks.json"
|
||||
"name": "Blocks PLA @base",
|
||||
"sub_path": "filament/Blocks PLA @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PLA-CF @Blocks",
|
||||
"sub_path": "filament/Generic PLA-CF @Blocks.json"
|
||||
"name": "Blocks PLA-CF @base",
|
||||
"sub_path": "filament/Blocks PLA-CF @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PVA @Blocks",
|
||||
"sub_path": "filament/Generic PVA @Blocks.json"
|
||||
"name": "Blocks PVA @base",
|
||||
"sub_path": "filament/Blocks PVA @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic TPU @Blocks",
|
||||
"sub_path": "filament/Generic TPU @Blocks.json"
|
||||
"name": "Blocks TPU @base",
|
||||
"sub_path": "filament/Blocks TPU @base.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ABS @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks ABS @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ABS @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks ABS @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ASA @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks ASA @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ASA @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks ASA @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ASA-CF @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks ASA-CF @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks ASA-CF @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks ASA-CF @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PA @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PA @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PA @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PA @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PA-CF @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PA-CF @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PA-CF @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PA-CF @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PC @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PC @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PC @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PC @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PETG @Blocks_Pro_S100",
|
||||
"sub_path": "filament/Blocks PETG @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PETG @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PETG @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PETG @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PETG @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PLA @Blocks_Pro_S100",
|
||||
"sub_path": "filament/Blocks PLA @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PLA @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PLA @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PLA @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PLA @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PLA-CF @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PLA-CF @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PLA-CF @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PLA-CF @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PVA @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks PVA @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks PVA @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks PVA @Blocks_RF50.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks TPU @Blocks_Pro_S100",
|
||||
"sub_path": "filament/Blocks TPU @Blocks_Pro_S100.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks TPU @Blocks_RD50_V2",
|
||||
"sub_path": "filament/Blocks TPU @Blocks_RD50_V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Blocks TPU @Blocks_RF50",
|
||||
"sub_path": "filament/Blocks TPU @Blocks_RF50.json"
|
||||
}
|
||||
],
|
||||
"machine_list": [
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ABS @Blocks_RD50_V2",
|
||||
"inherits": "Blocks ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "qhQ0qQDGJgsueCAF",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic ABS;Generic ABS @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ABS @Blocks_RF50",
|
||||
"inherits": "Blocks ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "bGYMFFdKiROKuSiY",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-13
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic ABS @Blocks",
|
||||
"name": "Blocks ABS @base",
|
||||
"inherits": "fdm_filament_abs",
|
||||
"renamed_from": "Blocks Generic ABS",
|
||||
"from": "system",
|
||||
"setting_id": "TL1Q1kA4t36GGObG",
|
||||
"filament_id": "OFY9muEs",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OFBBhiX8",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
@@ -81,13 +82,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ASA @Blocks_RD50_V2",
|
||||
"inherits": "Blocks ASA @base",
|
||||
"from": "system",
|
||||
"setting_id": "HW7xDXThvpVFbMq5",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic ASA;Generic ASA @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ASA @Blocks_RF50",
|
||||
"inherits": "Blocks ASA @base",
|
||||
"from": "system",
|
||||
"setting_id": "lBW7SCsvawi4icNU",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-13
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic ASA @Blocks",
|
||||
"name": "Blocks ASA @base",
|
||||
"inherits": "fdm_filament_asa",
|
||||
"renamed_from": "Blocks Generic ASA",
|
||||
"from": "system",
|
||||
"setting_id": "xP7cB4RHgbm4cYDD",
|
||||
"filament_id": "OFLPAxz3",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OFTz2YXa",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
@@ -81,13 +82,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ASA-CF @Blocks_RD50_V2",
|
||||
"inherits": "Blocks ASA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "x5ksnGLGGy1PgDns",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic ASA-CF;Generic ASA-CF @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks ASA-CF @Blocks_RF50",
|
||||
"inherits": "Blocks ASA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "pcbd5qFiMZAvDPg1",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-13
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic ASA-CF @Blocks",
|
||||
"name": "Blocks ASA-CF @base",
|
||||
"inherits": "fdm_filament_asa",
|
||||
"renamed_from": "Blocks Generic ASA-CF",
|
||||
"from": "system",
|
||||
"setting_id": "ju4ue7yJoz3ujny6",
|
||||
"filament_id": "OF53eLVD",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OF8b9I7o",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_type": [
|
||||
"ASA-CF"
|
||||
],
|
||||
@@ -84,13 +85,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PA @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PA @base",
|
||||
"from": "system",
|
||||
"setting_id": "gxD81n2jjrReLD29",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PA;Generic PA @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PA @Blocks_RF50",
|
||||
"inherits": "Blocks PA @base",
|
||||
"from": "system",
|
||||
"setting_id": "PzKxRMDnQUzVWRer",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PA @base",
|
||||
"inherits": "fdm_filament_pa",
|
||||
"from": "system",
|
||||
"filament_id": "OFidOgK4",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PA-CF @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "EtbeKx3rbhUo1TtM",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PA-CF;Generic PA-CF @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PA-CF @Blocks_RF50",
|
||||
"inherits": "Blocks PA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "udI8Q4X7HaPIaPt4",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-13
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PA-CF @Blocks",
|
||||
"name": "Blocks PA-CF @base",
|
||||
"inherits": "fdm_filament_pa",
|
||||
"renamed_from": "Blocks Generic PA-CF",
|
||||
"from": "system",
|
||||
"setting_id": "T6SO0WRdnG6wcyG8",
|
||||
"filament_id": "OFQLcbps",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OFdWrXpL",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_type": [
|
||||
"PA-CF"
|
||||
],
|
||||
@@ -87,13 +88,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"80"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PC @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PC @base",
|
||||
"from": "system",
|
||||
"setting_id": "HLc0shWnKwJzSFyW",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PC;Generic PC @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PC @Blocks_RF50",
|
||||
"inherits": "Blocks PC @base",
|
||||
"from": "system",
|
||||
"setting_id": "9a6bccZopMTeNHYu",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PC @base",
|
||||
"inherits": "fdm_filament_pc",
|
||||
"from": "system",
|
||||
"filament_id": "OFlGY0MN",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.94"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PETG @Blocks_Pro_S100",
|
||||
"inherits": "Blocks PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "AhMR49fcFqtrQ6hI",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PETG;Generic PETG @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PETG @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "3v7ah1zVG8MERPSJ",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PETG @Blocks_RF50",
|
||||
"inherits": "Blocks PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "S7weR19wo9meJ6Yz",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-18
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PETG @Blocks",
|
||||
"name": "Blocks PETG @base",
|
||||
"inherits": "fdm_filament_petg",
|
||||
"renamed_from": "Blocks Generic PETG",
|
||||
"from": "system",
|
||||
"setting_id": "NgzPlYItKHbmGgv3",
|
||||
"filament_id": "OFYPdQJh",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OF9t1Ga0",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.94"
|
||||
],
|
||||
@@ -81,18 +82,5 @@
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"5"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle",
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA @Blocks_Pro_S100",
|
||||
"inherits": "Blocks PLA @base",
|
||||
"from": "system",
|
||||
"setting_id": "vWojuLYIfduLGFLL",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PLA;Generic PLA @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PLA @base",
|
||||
"from": "system",
|
||||
"setting_id": "y7y8NcXNdp17sJUs",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA @Blocks_RF50",
|
||||
"inherits": "Blocks PLA @base",
|
||||
"from": "system",
|
||||
"setting_id": "jt1MJF4eZzkkscEJ",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-18
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PLA @Blocks",
|
||||
"name": "Blocks PLA @base",
|
||||
"inherits": "fdm_filament_pla",
|
||||
"renamed_from": "Blocks Generic PLA",
|
||||
"from": "system",
|
||||
"setting_id": "kjhuttx4T8OtTNmV",
|
||||
"filament_id": "OFDSrzZ8",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OFVGAC8g",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
@@ -39,18 +40,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle",
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA-CF @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PLA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "lhBtMKP1DmOB3HOI",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PLA-CF;Generic PLA-CF @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA-CF @Blocks_RF50",
|
||||
"inherits": "Blocks PLA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "6CU83mAyhcZ4FCPI",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PLA-CF @base",
|
||||
"inherits": "fdm_filament_pla",
|
||||
"from": "system",
|
||||
"filament_id": "OF6FfwOP",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA-CF"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"7"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PVA @Blocks_RD50_V2",
|
||||
"inherits": "Blocks PVA @base",
|
||||
"from": "system",
|
||||
"setting_id": "hREDqwKF6qyKkakp",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic PVA;Generic PVA @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PVA @Blocks_RF50",
|
||||
"inherits": "Blocks PVA @base",
|
||||
"from": "system",
|
||||
"setting_id": "q6pKqHY7Iz4OZaAC",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks PVA @base",
|
||||
"inherits": "fdm_filament_pva",
|
||||
"from": "system",
|
||||
"filament_id": "OFqcNuE1",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks TPU @Blocks_Pro_S100",
|
||||
"inherits": "Blocks TPU @base",
|
||||
"from": "system",
|
||||
"setting_id": "6Rkz2je91JRTRoTi",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
],
|
||||
"renamed_from": "Blocks Generic TPU;Generic TPU @Blocks"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks TPU @Blocks_RD50_V2",
|
||||
"inherits": "Blocks TPU @base",
|
||||
"from": "system",
|
||||
"setting_id": "mkCqRQTtXldzoEUg",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Blocks TPU @Blocks_RF50",
|
||||
"inherits": "Blocks TPU @base",
|
||||
"from": "system",
|
||||
"setting_id": "bCCvu5Ow1JkwIrRR",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
+6
-18
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic TPU @Blocks",
|
||||
"name": "Blocks TPU @base",
|
||||
"inherits": "fdm_filament_tpu",
|
||||
"renamed_from": "Blocks Generic TPU",
|
||||
"from": "system",
|
||||
"setting_id": "6iVYOCZo8ugs4JA0",
|
||||
"filament_id": "OFgbpcy9",
|
||||
"instantiation": "true",
|
||||
"filament_id": "OFYnedOZ",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"Blocks"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"4"
|
||||
],
|
||||
@@ -54,18 +55,5 @@
|
||||
],
|
||||
"during_print_exhaust_fan_speed": [
|
||||
"80"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle",
|
||||
"BLOCKS Pro S100 0.6 nozzle",
|
||||
"BLOCKS Pro S100 0.8 nozzle",
|
||||
"BLOCKS Pro S100 1.0 nozzle",
|
||||
"BLOCKS Pro S100 1.2 nozzle",
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PA @Blocks",
|
||||
"inherits": "fdm_filament_pa",
|
||||
"renamed_from": "Blocks Generic PA",
|
||||
"from": "system",
|
||||
"setting_id": "hL3p8gAeciwDkjNK",
|
||||
"filament_id": "OFg8ndtj",
|
||||
"instantiation": "true",
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PC @Blocks",
|
||||
"inherits": "fdm_filament_pc",
|
||||
"renamed_from": "Blocks Generic PC",
|
||||
"from": "system",
|
||||
"setting_id": "Xhs8PjtmwwVj0ksE",
|
||||
"filament_id": "OFLakOUI",
|
||||
"instantiation": "true",
|
||||
"filament_flow_ratio": [
|
||||
"0.94"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PLA-CF @Blocks",
|
||||
"inherits": "fdm_filament_pla",
|
||||
"renamed_from": "Blocks Generic PLA-CF",
|
||||
"from": "system",
|
||||
"setting_id": "oKUqzwAWMrpiyGLf",
|
||||
"filament_id": "OFWbdGsC",
|
||||
"instantiation": "true",
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA-CF"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"7"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PVA @Blocks",
|
||||
"inherits": "fdm_filament_pva",
|
||||
"renamed_from": "Blocks Generic PVA",
|
||||
"from": "system",
|
||||
"setting_id": "tNw3oe7VovLkgUbB",
|
||||
"filament_id": "OFDvXujf",
|
||||
"instantiation": "true",
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
],
|
||||
"compatible_printers": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"setting_id": "G7HQgaspHXduDM8O",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BLOCKS Pro S100",
|
||||
"default_print_profile": "0.20mm Standard 0.4 nozzle @Blocks",
|
||||
"default_print_profile": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
|
||||
"max_layer_height": [
|
||||
"0.30"
|
||||
],
|
||||
@@ -25,5 +25,8 @@
|
||||
"printable_height": "900",
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS Pro S100"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_Pro_S100"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"setting_id": "XkWdS210oUMQN17N",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BLOCKS Pro S100",
|
||||
"default_print_profile": "0.30mm Standard 0.6 nozzle @Blocks",
|
||||
"default_print_profile": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
|
||||
"max_layer_height": [
|
||||
"0.50"
|
||||
],
|
||||
@@ -26,5 +26,8 @@
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS Pro S100"
|
||||
],
|
||||
"printer_variant": "0.6"
|
||||
"printer_variant": "0.6",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_Pro_S100"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"setting_id": "I6PdeCS7SbvIlkZf",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BLOCKS Pro S100",
|
||||
"default_print_profile": "0.40mm Standard 0.8 nozzle @Blocks",
|
||||
"default_print_profile": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
|
||||
"max_layer_height": [
|
||||
"0.56"
|
||||
],
|
||||
@@ -26,5 +26,8 @@
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS Pro S100"
|
||||
],
|
||||
"printer_variant": "0.8"
|
||||
"printer_variant": "0.8",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_Pro_S100"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"setting_id": "elLdUyy9RhUoa37o",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BLOCKS Pro S100",
|
||||
"default_print_profile": "0.50mm Standard 1.0 nozzle @Blocks",
|
||||
"default_print_profile": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
|
||||
"max_layer_height": [
|
||||
"0.7"
|
||||
],
|
||||
@@ -26,5 +26,8 @@
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS Pro S100"
|
||||
],
|
||||
"printer_variant": "1.0"
|
||||
"printer_variant": "1.0",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_Pro_S100"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"setting_id": "xtsGSJ4JkbHTPi44",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BLOCKS Pro S100",
|
||||
"default_print_profile": "0.60mm Standard 1.2 nozzle @Blocks",
|
||||
"default_print_profile": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
|
||||
"max_layer_height": [
|
||||
"0.8"
|
||||
],
|
||||
@@ -26,5 +26,8 @@
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS Pro S100"
|
||||
],
|
||||
"printer_variant": "1.2"
|
||||
"printer_variant": "1.2",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_Pro_S100"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"bed_model": "PRO S100 HotBed model.stl",
|
||||
"bed_texture": "PRO S100 HotBed texture.svg",
|
||||
"hotend_model": "BLOCKS PrintCore.stl",
|
||||
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
|
||||
"default_materials": "Blocks PLA @Blocks_Pro_S100;Blocks PETG @Blocks_Pro_S100;Blocks TPU @Blocks_Pro_S100"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,9 @@
|
||||
"printable_height": "500",
|
||||
"upward_compatible_machine": [
|
||||
"BLOCKS RD50 V2"
|
||||
],
|
||||
"printer_variant": "0.4",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RD50_V2"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -35,5 +35,8 @@
|
||||
"deretraction_speed": [
|
||||
"80"
|
||||
],
|
||||
"printer_variant": "0.6"
|
||||
"printer_variant": "0.6",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RD50_V2"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -35,5 +35,8 @@
|
||||
"deretraction_speed": [
|
||||
"80"
|
||||
],
|
||||
"printer_variant": "0.8"
|
||||
"printer_variant": "0.8",
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RD50_V2"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"bed_model": "RD50 V2 HotBed model.stl",
|
||||
"bed_texture": "RD50 V2 HotBed texture.svg",
|
||||
"hotend_model": "BLOCKS PrintCore.stl",
|
||||
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
|
||||
"default_materials": "Blocks PLA @Blocks_RD50_V2;Blocks PETG @Blocks_RD50_V2;Blocks TPU @Blocks_RD50_V2"
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"12"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1"
|
||||
"1.2"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"60"
|
||||
@@ -79,5 +79,10 @@
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RF50"
|
||||
],
|
||||
"support_multi_bed_types": "1",
|
||||
"default_bed_type": "Textured PEI Plate"
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"12"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1"
|
||||
"1.2"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"60"
|
||||
@@ -75,8 +75,8 @@
|
||||
"BLOCKS RF50"
|
||||
],
|
||||
"machine_start_gcode": [
|
||||
"PRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer]",
|
||||
"SET_PRESSURE_ADVANCE ADVANCE=0.046"
|
||||
"PRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer]\n",
|
||||
"SET_PRESSURE_ADVANCE ADVANCE=0.046\n"
|
||||
],
|
||||
"printer_variant": "0.6",
|
||||
"wipe_distance": [
|
||||
@@ -84,5 +84,10 @@
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RF50"
|
||||
],
|
||||
"support_multi_bed_types": "1",
|
||||
"default_bed_type": "Textured PEI Plate"
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"12"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1"
|
||||
"1.2"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"60"
|
||||
@@ -80,5 +80,10 @@
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"0"
|
||||
]
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Blocks PLA @Blocks_RF50"
|
||||
],
|
||||
"support_multi_bed_types": "1",
|
||||
"default_bed_type": "Textured PEI Plate"
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"bed_model": "RF50 HotBed model.stl",
|
||||
"bed_texture": "RF50 HotBed texture.svg",
|
||||
"hotend_model": "BLOCKS PrintCore.stl",
|
||||
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
|
||||
"default_materials": "Blocks PLA @Blocks_RF50;Blocks PETG @Blocks_RF50;Blocks TPU @Blocks_RF50",
|
||||
"not_support_bed_type": "Smooth Cool Plate;Engineering Plate;Textured Cool Plate;Cool Plate (SuperTack)"
|
||||
}
|
||||
|
||||
@@ -123,9 +123,6 @@
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @Blocks"
|
||||
],
|
||||
"bed_exclude_area": [
|
||||
"0x0"
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.20mm Standard 0.4 nozzle @Blocks",
|
||||
"name": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_blocks_common",
|
||||
"from": "system",
|
||||
"setting_id": "47aDtVL6E5DVr7Mt",
|
||||
"setting_id": "QSEguMMChNFZsK5H",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.2",
|
||||
"bottom_shell_layers": "4",
|
||||
"top_shell_layers": "5",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.20mm Standard 0.4 nozzle @Blocks"
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -61,5 +61,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.24mm Draft 0.4 nozzle @Blocks",
|
||||
"name": "0.24mm Draft 0.4 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_blocks_common",
|
||||
"from": "system",
|
||||
"setting_id": "ETADjxV6ViJQdnHA",
|
||||
"setting_id": "uALfEFHGmup6vBYI",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.24",
|
||||
"bottom_shell_layers": "4",
|
||||
"top_shell_layers": "5",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.24mm Draft 0.4 nozzle @Blocks"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks",
|
||||
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_blocks_common",
|
||||
"from": "system",
|
||||
"setting_id": "UbuICsmppKDsaaZ4",
|
||||
"setting_id": "HYKF5l2OGvpugSQy",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "4",
|
||||
"layer_height": "0.30",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.4 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.30mm Extra Draft 0.4 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.30mm Optimal 0.8 nozzle @Blocks",
|
||||
"name": "0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 0.8 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "YqkYzp5zdH4R5upm",
|
||||
"setting_id": "OUfZvizM5MGnvHfp",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.3",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.8 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.30mm Optimal 0.8 nozzle @Blocks"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.30mm Optimal 1.0 nozzle @Blocks",
|
||||
"name": "0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.0 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "8pBawPScHkyQu0SW",
|
||||
"setting_id": "HGG9WxKVr89qNFDx",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "4",
|
||||
"top_shell_layers": "4",
|
||||
"layer_height": "0.30",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.0 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.30mm Optimal 1.0 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.30mm Standard 0.6 nozzle @Blocks",
|
||||
"name": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 0.6 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "EPI3PAVK9LpJOZWO",
|
||||
"setting_id": "gO2I1YjBl4nf8yzr",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.30",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.6 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.30mm Standard 0.6 nozzle @Blocks"
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.40mm Draft 0.6 nozzle @Blocks",
|
||||
"name": "0.40mm Draft 0.6 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 0.6 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "CShVKoGspbMJNPw8",
|
||||
"setting_id": "OjsLUl1heqv1cevs",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "2",
|
||||
"layer_height": "0.40",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.6 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.40mm Draft 0.6 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.40mm Standard 0.8 nozzle @Blocks",
|
||||
"name": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 0.8 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "TSRjmfWaatY7oLJB",
|
||||
"setting_id": "laOTioBGySeudJpl",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "4",
|
||||
"layer_height": "0.40",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.8 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.40mm Standard 0.8 nozzle @Blocks"
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.50mm Draft 0.8 nozzle @Blocks",
|
||||
"name": "0.50mm Draft 0.8 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 0.8 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "vOtZHrDS88Shmf2m",
|
||||
"setting_id": "oAhgUZdADR7kP87e",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "4",
|
||||
"layer_height": "0.50",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 0.8 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.50mm Draft 0.8 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.50mm Optimal 1.2 nozzle @Blocks",
|
||||
"name": "0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.2 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "BKrEAKqDYzdH43wc",
|
||||
"setting_id": "0eqNgX26jemsBfV5",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.50",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.50mm Optimal 1.2 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.50mm Standard 1.0 nozzle @Blocks",
|
||||
"name": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.0 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "D40iksDm7mFjhSKn",
|
||||
"setting_id": "z1qCW1cEMc4QvBbV",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.50",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.0 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.50mm Standard 1.0 nozzle @Blocks"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"top_solid_infill_flow_ratio": "0.97",
|
||||
"bottom_solid_infill_flow_ratio": "1.08",
|
||||
"seam_position": "aligned",
|
||||
"sparse_infill_patter": "grid",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"initial_layer_speed": "60",
|
||||
"initial_layer_infill_speed": "80",
|
||||
"initial_travel_speed": "100%",
|
||||
@@ -57,5 +57,7 @@
|
||||
"accel_to_decel_enable": "1",
|
||||
"accel_to_decel_factor": "50%",
|
||||
"elefant_foot_compensation": "0.25",
|
||||
"elefant_foot_compensation_layers": "1"
|
||||
"elefant_foot_compensation_layers": "1",
|
||||
"skirt_loops": "2",
|
||||
"skirt_type": "perobject"
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.60mm Draft 1.0 nozzle @Blocks",
|
||||
"name": "0.60mm Draft 1.0 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.0 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "UJUe8XCBzZf8NyCL",
|
||||
"setting_id": "gTTgtfG8qb9My3hH",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.60",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.0 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.60mm Draft 1.0 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.60mm Standard 1.2 nozzle @Blocks",
|
||||
"name": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.2 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "PSBFrTykpkytPI2Q",
|
||||
"setting_id": "ApcyToENCIANrI71",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.60",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.60mm Standard 1.2 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.70mm Draft 1.2 nozzle @Blocks",
|
||||
"name": "0.70mm Draft 1.2 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.2 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "0ZQ0USGX9SwAKr29",
|
||||
"setting_id": "DEv7X7zaTfxRQmjo",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.70",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.70mm Draft 1.2 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks",
|
||||
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.0 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "69PFmjOVFBOIp3Qg",
|
||||
"setting_id": "nUW7N9dJmTyQT4AG",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "3",
|
||||
"layer_height": "0.70",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.0 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.70mm Extra Draft 1.0 nozzle @Blocks"
|
||||
}
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks",
|
||||
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100",
|
||||
"inherits": "fdm_process_common 1.2 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "iBwRI5JJewW5HJSv",
|
||||
"setting_id": "JMy62SonRfK0cAdL",
|
||||
"instantiation": "true",
|
||||
"bottom_shell_layers": "2",
|
||||
"top_shell_layers": "2",
|
||||
"layer_height": "0.80",
|
||||
"compatible_printers": [
|
||||
"BLOCKS Pro S100 1.2 nozzle"
|
||||
]
|
||||
],
|
||||
"renamed_from": "0.80mm Extra Draft 1.2 nozzle @Blocks"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "OrcaFilamentLibrary",
|
||||
"version": "02.04.00.12",
|
||||
"version": "02.04.00.13",
|
||||
"force_update": "0",
|
||||
"description": "Orca Filament Library",
|
||||
"filament_list": [
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"filament_id": "OFvgE0Zh",
|
||||
"instantiation": "false",
|
||||
"fan_cooling_layer_time": [
|
||||
"80"
|
||||
"81"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"50"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
<script type="text/javascript" src="../include/jquery-2.1.1.min.js"></script>
|
||||
<script type="text/javascript" src="../include/json2.js"></script>
|
||||
<script type="text/javascript" src="../include/globalapi.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../include/swiper/swiper-bundle.min.css" />
|
||||
<script type="text/javascript" src="../include/swiper/swiper-bundle.min.js"></script>
|
||||
@@ -18,6 +17,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="model.css" />
|
||||
<link rel="stylesheet" type="text/css" href="./css/dark.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../include/global.css" /> <!-- ORCA One for all-->
|
||||
<script type="text/javascript" src="../include/globalapi.js"></script>
|
||||
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<script type="text/javascript" src="model.js"></script>
|
||||
|
||||
@@ -697,7 +697,8 @@ public:
|
||||
}
|
||||
} else {
|
||||
// Resize by duplicating the last value.
|
||||
this->values.resize(n, this->values./*back*/front());
|
||||
T v = this->values./*back*/front();
|
||||
this->values.resize(n, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -772,8 +773,10 @@ public:
|
||||
|
||||
if (this->values.empty())
|
||||
this->values.resize(rhs_vec->size());
|
||||
else
|
||||
this->values.resize(rhs_vec->size(), this->values.front());
|
||||
else {
|
||||
T v = this->values.front();
|
||||
this->values.resize(rhs_vec->size(), v);
|
||||
}
|
||||
|
||||
assert(default_index.size() == rhs_vec->size());
|
||||
|
||||
|
||||
@@ -990,6 +990,7 @@ EmbossStyles Emboss::get_font_list_by_enumeration() {
|
||||
std::vector<std::wstring> font_names;
|
||||
EnumFontFamilies(hDC, (LPCTSTR) NULL, EnumFamCallBack,
|
||||
(LPARAM) &font_names);
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
EmbossStyles font_list;
|
||||
for (const std::wstring &font_name : font_names) {
|
||||
|
||||
@@ -464,6 +464,16 @@ void group_region_by_fuzzify(PerimeterGenerator& g)
|
||||
}
|
||||
}
|
||||
|
||||
g.fuzzy_supported_area.reset();
|
||||
if ((g.has_fuzzy_skin || g.has_fuzzy_hole) && g.lower_slices != nullptr) {
|
||||
coord_t max_thickness = 0;
|
||||
for (const auto& region : regions)
|
||||
if (should_fuzzify(region.config, g.layer_id, 0, true) || should_fuzzify(region.config, g.layer_id, 0, false))
|
||||
max_thickness = std::max(max_thickness, region.config.thickness);
|
||||
// Walls farther than a line width plus the noise amplitude from the layer below are bridging; keep them smooth.
|
||||
g.fuzzy_supported_area = offset_ex(*g.lower_slices, float(g.ext_perimeter_flow.scaled_width() + max_thickness));
|
||||
}
|
||||
|
||||
if (regions.size() == 1) { // optimization
|
||||
g.regions_by_fuzzify.push_back({regions.front().config, {}});
|
||||
return;
|
||||
@@ -560,13 +570,23 @@ static std::vector<MergedFuzzyRegion> collect_merged_fuzzy_regions(const std::ve
|
||||
return merged_regions;
|
||||
}
|
||||
|
||||
// Afterwards an empty region means nothing to fuzzify, no longer full coverage.
|
||||
static void restrict_to_supported(std::vector<MergedFuzzyRegion>& merged_regions, const std::optional<ExPolygons>& supported)
|
||||
{
|
||||
if (!supported)
|
||||
return;
|
||||
for (auto& merged_region : merged_regions)
|
||||
merged_region.expolygons = merged_region.expolygons.empty() ? *supported : intersection_ex(merged_region.expolygons, *supported);
|
||||
}
|
||||
|
||||
Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perimeter_generator, const size_t loop_idx, const bool is_contour)
|
||||
{
|
||||
Polygon fuzzified;
|
||||
|
||||
const auto slice_z = perimeter_generator.slice_z;
|
||||
const auto& regions = perimeter_generator.regions_by_fuzzify;
|
||||
if (regions.size() == 1) { // optimization
|
||||
const auto& supported = perimeter_generator.fuzzy_supported_area;
|
||||
if (regions.size() == 1 && !supported) { // optimization
|
||||
const auto& config = regions.begin()->first;
|
||||
const bool fuzzify = should_fuzzify(config, perimeter_generator.layer_id, loop_idx, is_contour);
|
||||
if (!fuzzify) {
|
||||
@@ -590,7 +610,7 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
|
||||
// Fast path: single merged region — apply directly without splitting
|
||||
if (merged_regions.size() == 1) {
|
||||
const auto& mr = merged_regions.front();
|
||||
if (mr.expolygons.empty()) {
|
||||
if (mr.expolygons.empty() && !supported) {
|
||||
fuzzified = polygon;
|
||||
fuzzy_polyline(fuzzified.points, true, slice_z, *mr.config);
|
||||
return fuzzified;
|
||||
@@ -626,6 +646,8 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
|
||||
if (!merged_regions[i].expolygons.empty() && !merged_regions[j].expolygons.empty())
|
||||
merged_regions[i].expolygons = diff_ex(merged_regions[i].expolygons, merged_regions[j].expolygons);
|
||||
|
||||
restrict_to_supported(merged_regions, supported);
|
||||
|
||||
// Split the loops into lines with different config, and fuzzy them separately
|
||||
fuzzified = polygon;
|
||||
for (const auto& r : merged_regions) {
|
||||
@@ -689,7 +711,8 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
|
||||
const auto slice_z = perimeter_generator.slice_z;
|
||||
const auto layer_height = perimeter_generator.layer_height;
|
||||
const auto& regions = perimeter_generator.regions_by_fuzzify;
|
||||
if (regions.size() == 1) { // optimization
|
||||
const auto& supported = perimeter_generator.fuzzy_supported_area;
|
||||
if (regions.size() == 1 && !supported) { // optimization
|
||||
const auto& config = regions.begin()->first;
|
||||
const bool fuzzify = should_fuzzify(config, perimeter_generator.layer_id, extrusion->inset_idx, is_contour);
|
||||
if (fuzzify)
|
||||
@@ -703,7 +726,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
|
||||
if (!merged_regions.empty()) {
|
||||
|
||||
// Fast path: single merged region — apply directly without splitting
|
||||
if (merged_regions.size() == 1 && merged_regions.front().expolygons.empty()) {
|
||||
if (merged_regions.size() == 1 && merged_regions.front().expolygons.empty() && !supported) {
|
||||
fuzzy_extrusion_line(extrusion->junctions, slice_z, perimeter_generator.layer_height, *merged_regions.front().config, closed);
|
||||
return;
|
||||
}
|
||||
@@ -753,6 +776,8 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
|
||||
if (!merged_regions[i].expolygons.empty() && !merged_regions[j].expolygons.empty())
|
||||
merged_regions[i].expolygons = diff_ex(merged_regions[i].expolygons, merged_regions[j].expolygons);
|
||||
|
||||
restrict_to_supported(merged_regions, supported);
|
||||
|
||||
// Split the loops into lines with different config, and fuzzy them separately
|
||||
for (const auto& r : merged_regions) {
|
||||
const auto splitted = Algorithm::split_line(*extrusion, r.expolygons, false);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "../AABBTreeLines.hpp"
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
@@ -14,7 +15,9 @@
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
|
||||
// Boost pool: Don't use mutexes to synchronize memory allocation.
|
||||
#define BOOST_POOL_NO_MT
|
||||
@@ -1318,6 +1321,564 @@ bool has_no_collinear_lines(const Polylines &polylines)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Non-crossing centerlines for multiline adaptive cubic, see docs/HLSD/multiline-infill.md.
|
||||
namespace noncrossing {
|
||||
|
||||
// y = x() * x + y() in the frame where the sweep family is horizontal.
|
||||
using Lin = Vec2d;
|
||||
|
||||
static const Vec2d family_dir[3] { Vec2d(1., 0.), Vec2d(0.5, 0.5 * sqrt(3.)), Vec2d(0.5, -0.5 * sqrt(3.)) };
|
||||
|
||||
struct SweepLine
|
||||
{
|
||||
Vec2d a, b; // a.x() < b.x()
|
||||
int family;
|
||||
Lin lin;
|
||||
std::vector<std::pair<double, int>> junctions; // (x, junction)
|
||||
};
|
||||
|
||||
struct Junction
|
||||
{
|
||||
Vec2d p;
|
||||
std::vector<int> lines;
|
||||
std::vector<std::pair<int, int>> pairs; // (left, right) line of each path through, bottom-up
|
||||
std::vector<std::pair<int, int>> bends; // (path, bend) of each pair, -1 where it runs straight
|
||||
};
|
||||
|
||||
struct LevelPath
|
||||
{
|
||||
std::vector<Vec2d> verts; // start, bends, end
|
||||
std::vector<int> lines; // line of each piece
|
||||
std::vector<int> junctions; // junction of each bend
|
||||
std::vector<int> turn; // 1 turning up, -1 turning down
|
||||
std::vector<std::vector<Lin>> cuts;
|
||||
std::vector<std::pair<Lin, int>> pushes; // (line, bend) keeping a wall away from a neighbour's cut
|
||||
int start_term { -1 }; // junction where the path stops on another line, or -1
|
||||
int end_term { -1 };
|
||||
};
|
||||
|
||||
// Moves f onto line c (side 1: from below) wherever c lies beyond it, over the stretches overlapping [w0, w1].
|
||||
static void clip_profile(std::vector<Vec2d> &f, const Lin &c, int side, double w0, double w1, double lim0, double lim1, bool cut_at_window, bool drop_past_limits)
|
||||
{
|
||||
const double r0 = std::max(lim0, f.front().x()), r1 = std::min(lim1, f.back().x());
|
||||
if (r1 <= r0)
|
||||
return;
|
||||
const size_t ia = std::upper_bound(f.begin(), f.end(), r0, [](double x, const Vec2d &p) { return x < p.x(); }) - f.begin();
|
||||
const size_t ib = std::lower_bound(f.begin() + ia, f.end(), r1, [](const Vec2d &p, double x) { return p.x() < x; }) - f.begin();
|
||||
auto interpolate = [](const Vec2d &a, const Vec2d &b, double x) { return b.x() > a.x() ? a.y() + (x - a.x()) / (b.x() - a.x()) * (b.y() - a.y()) : b.y(); };
|
||||
std::vector<Vec2d> local{ Vec2d(r0, interpolate(f[ia - 1], f[ia], r0)) };
|
||||
local.insert(local.end(), f.begin() + ia, f.begin() + ib);
|
||||
local.emplace_back(r1, interpolate(f[ib - 1], f[ib], r1));
|
||||
|
||||
const double tol = 1.;
|
||||
auto beyond = [&c, side, tol](const Vec2d &p) { return side * (c.x() * p.x() + c.y() - p.y()) - tol; };
|
||||
std::vector<std::pair<double, double>> stretches;
|
||||
auto add = [&stretches](double x0, double x1) {
|
||||
if (!stretches.empty() && stretches.back().second >= x0)
|
||||
stretches.back().second = x1;
|
||||
else
|
||||
stretches.emplace_back(x0, x1);
|
||||
};
|
||||
for (size_t i = 1; i < local.size(); ++i) {
|
||||
const Vec2d &p = local[i - 1], &q = local[i];
|
||||
if (q.x() <= p.x())
|
||||
continue;
|
||||
const double bp = beyond(p), bq = beyond(q);
|
||||
if (bp > 0. && bq > 0.)
|
||||
add(p.x(), q.x());
|
||||
else if (bp > 0. || bq > 0.) {
|
||||
const double x = p.x() + bp / (bp - bq) * (q.x() - p.x());
|
||||
if (bp > 0.)
|
||||
add(p.x(), x);
|
||||
else
|
||||
add(x, q.x());
|
||||
}
|
||||
}
|
||||
std::vector<std::pair<double, double>> keep;
|
||||
for (auto [x0, x1] : stretches) {
|
||||
if (x1 < w0 || x0 > w1)
|
||||
continue;
|
||||
if (drop_past_limits && ((x0 <= r0 && r0 == lim0) || (x1 >= r1 && r1 == lim1)))
|
||||
continue;
|
||||
keep.emplace_back(cut_at_window ? std::max(x0, w0) : x0, cut_at_window ? std::min(x1, w1) : x1);
|
||||
}
|
||||
if (keep.empty())
|
||||
return;
|
||||
|
||||
auto y_local = [&](double x) {
|
||||
size_t i = 1;
|
||||
while (i + 1 < local.size() && local[i].x() < x)
|
||||
++i;
|
||||
return interpolate(local[i - 1], local[i], x);
|
||||
};
|
||||
std::vector<Vec2d> out(f.begin(), f.begin() + ia);
|
||||
auto push = [&out, tol](double x, double y) {
|
||||
if (out.empty() || x > out.back().x() || std::abs(y - out.back().y()) > 2. * tol)
|
||||
out.emplace_back(x, y);
|
||||
};
|
||||
size_t k = 0;
|
||||
for (const Vec2d &p : local) {
|
||||
for (; k < keep.size() && keep[k].second < p.x(); ++k) {
|
||||
const auto [x0, x1] = keep[k];
|
||||
push(x0, y_local(x0));
|
||||
push(x0, c.x() * x0 + c.y());
|
||||
push(x1, c.x() * x1 + c.y());
|
||||
push(x1, y_local(x1));
|
||||
}
|
||||
if (k < keep.size() && keep[k].first <= p.x() && p.x() <= keep[k].second)
|
||||
continue;
|
||||
push(p.x(), p.y());
|
||||
}
|
||||
for (; k < keep.size(); ++k) {
|
||||
const auto [x0, x1] = keep[k];
|
||||
push(x0, y_local(x0));
|
||||
push(x0, c.x() * x0 + c.y());
|
||||
push(x1, c.x() * x1 + c.y());
|
||||
push(x1, y_local(x1));
|
||||
}
|
||||
for (size_t i = ib; i < f.size(); ++i)
|
||||
push(f[i].x(), f[i].y());
|
||||
f = std::move(out);
|
||||
}
|
||||
|
||||
static std::vector<Vec2d> path_points(const LevelPath &path, const std::vector<SweepLine> &lines, double reach)
|
||||
{
|
||||
std::vector<Vec2d> f = path.verts;
|
||||
const int nb = int(path.junctions.size());
|
||||
auto x_of = [&path](int b) { return path.verts[b + 1].x(); };
|
||||
auto sharp = [&](int b) { return lines[path.lines[b]].family != 0 && lines[path.lines[b + 1]].family != 0; };
|
||||
// The run of bends turning the same way as bend b, up to the neighbouring bends turning the other way.
|
||||
auto window = [&](int b) {
|
||||
int l = b - 1, r = b + 1;
|
||||
while (l >= 0 && path.turn[l] == path.turn[b])
|
||||
--l;
|
||||
while (r < nb && path.turn[r] == path.turn[b])
|
||||
++r;
|
||||
return std::make_pair(l >= 0 ? x_of(l) : f.front().x(), r < nb ? x_of(r) : f.back().x());
|
||||
};
|
||||
// Sharp bends between the slanted lines go last, so they win at the tip of a small triangle.
|
||||
for (int b = 0; b < nb; ++b)
|
||||
if (!sharp(b)) {
|
||||
const auto [w0, w1] = window(b);
|
||||
for (const Lin &c : path.cuts[b])
|
||||
clip_profile(f, c, path.turn[b], w0, w1, x_of(b) - reach, x_of(b) + reach, true, false);
|
||||
}
|
||||
for (int b = 0; b < nb; ++b)
|
||||
if (sharp(b))
|
||||
for (const Lin &c : path.cuts[b])
|
||||
clip_profile(f, c, path.turn[b], x_of(b), x_of(b), x_of(b) - reach, x_of(b) + reach, false, true);
|
||||
for (const auto &[c, b] : path.pushes) {
|
||||
const auto [w0, w1] = window(b);
|
||||
clip_profile(f, c, path.turn[b], w0, w1, x_of(b) - reach, x_of(b) + reach, true, true);
|
||||
}
|
||||
std::vector<Vec2d> pts;
|
||||
for (const Vec2d &p : f) {
|
||||
while (pts.size() >= 2 && std::abs(cross2(Vec2d(pts.back() - pts[pts.size() - 2]), Vec2d(p - pts.back()))) <=
|
||||
1e-9 * (pts.back() - pts[pts.size() - 2]).norm() * (p - pts.back()).norm())
|
||||
pts.pop_back();
|
||||
pts.push_back(p);
|
||||
}
|
||||
return pts;
|
||||
}
|
||||
|
||||
static double polyline_length(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
double len = 0.;
|
||||
for (size_t i = 1; i < pts.size(); ++i)
|
||||
len += (pts[i] - pts[i - 1]).norm();
|
||||
return len;
|
||||
}
|
||||
|
||||
// Point at the given distance along pts, and the index of the segment it lies on.
|
||||
static std::pair<Vec2d, size_t> point_along(const std::vector<Vec2d> &pts, double t)
|
||||
{
|
||||
for (size_t i = 1; i < pts.size(); ++i) {
|
||||
const double len = (pts[i] - pts[i - 1]).norm();
|
||||
if (t <= len)
|
||||
return { pts[i - 1] + (len > 0. ? t / len : 0.) * (pts[i] - pts[i - 1]), i };
|
||||
t -= len;
|
||||
}
|
||||
return { pts.back(), pts.size() - 1 };
|
||||
}
|
||||
|
||||
} // namespace noncrossing
|
||||
|
||||
Polylines multiline_paths(const Lines &lines_in, double d1, double end_overlap, int sweep, const BoundingBox &cover)
|
||||
{
|
||||
using namespace noncrossing;
|
||||
const double eps = scale_(0.002);
|
||||
const Eigen::Rotation2Dd to_sweep(-sweep * M_PI / 3.);
|
||||
const BoundingBoxf box(cover.min.cast<double>(), cover.max.cast<double>());
|
||||
|
||||
// Lines in the sweep frame, collinear pieces merged.
|
||||
struct Piece { double c, s0, s1; };
|
||||
std::array<std::vector<Piece>, 3> pieces;
|
||||
for (const Line &line : lines_in) {
|
||||
Vec2d a = line.a.cast<double>(), b = line.b.cast<double>();
|
||||
if (!Geometry::liang_barsky_line_clipping(a, b, box) || (b - a).norm() < 10. * eps)
|
||||
continue;
|
||||
a = to_sweep * a;
|
||||
b = to_sweep * b;
|
||||
const double angle = std::atan2(b.y() - a.y(), b.x() - a.x()) / (M_PI / 3.);
|
||||
if (std::abs(angle - std::round(angle)) > 0.01)
|
||||
// Not one of the three families.
|
||||
return {};
|
||||
const int f = (int(std::round(angle)) % 3 + 3) % 3;
|
||||
const Vec2d &d = family_dir[f];
|
||||
const Vec2d n(-d.y(), d.x());
|
||||
pieces[f].push_back({ n.dot(a), std::min(d.dot(a), d.dot(b)), std::max(d.dot(a), d.dot(b)) });
|
||||
}
|
||||
std::vector<SweepLine> lines;
|
||||
for (int f = 0; f < 3; ++f) {
|
||||
std::vector<Piece> &ps = pieces[f];
|
||||
const Vec2d &d = family_dir[f];
|
||||
const Vec2d n(-d.y(), d.x());
|
||||
const double slope = d.y() / d.x();
|
||||
std::sort(ps.begin(), ps.end(), [](const Piece &l, const Piece &r) { return l.c < r.c; });
|
||||
for (size_t i = 0; i < ps.size();) {
|
||||
size_t j = i + 1;
|
||||
while (j < ps.size() && ps[j].c - ps[i].c < eps)
|
||||
++j;
|
||||
std::sort(ps.begin() + i, ps.begin() + j, [](const Piece &l, const Piece &r) { return l.s0 < r.s0; });
|
||||
double c = 0.;
|
||||
for (size_t k = i; k < j; ++k)
|
||||
c += ps[k].c / double(j - i);
|
||||
double s0 = ps[i].s0, s1 = ps[i].s1;
|
||||
for (size_t k = i + 1; k <= j; ++k) {
|
||||
if (k < j && ps[k].s0 <= s1 + eps) {
|
||||
s1 = std::max(s1, ps[k].s1);
|
||||
continue;
|
||||
}
|
||||
const Vec2d a = s0 * d + c * n;
|
||||
lines.push_back({ a, s1 * d + c * n, f, Lin(slope, a.y() - slope * a.x()), {} });
|
||||
if (k < j) {
|
||||
s0 = ps[k].s0;
|
||||
s1 = ps[k].s1;
|
||||
}
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
auto along = [](const SweepLine &l, const Vec2d &p) { return family_dir[l.family].dot(p - l.a); };
|
||||
auto length = [](const SweepLine &l) { return (l.b - l.a).norm(); };
|
||||
|
||||
// Crossings, including the ends of lines stopping on another line.
|
||||
std::vector<Junction> junctions;
|
||||
auto detect_junctions = [&]() {
|
||||
struct Hit { Vec2d p; int i, j; };
|
||||
std::vector<Hit> hits;
|
||||
std::vector<int> order(lines.size());
|
||||
std::iota(order.begin(), order.end(), 0);
|
||||
std::sort(order.begin(), order.end(), [&lines](int l, int r) { return lines[l].a.x() < lines[r].a.x(); });
|
||||
for (size_t oi = 0; oi < order.size(); ++oi) {
|
||||
const SweepLine &li = lines[order[oi]];
|
||||
for (size_t oj = oi + 1; oj < order.size() && lines[order[oj]].a.x() <= li.b.x() + eps; ++oj) {
|
||||
const SweepLine &lj = lines[order[oj]];
|
||||
if (li.family == lj.family)
|
||||
continue;
|
||||
const double x = (lj.lin.y() - li.lin.y()) / (li.lin.x() - lj.lin.x());
|
||||
const Vec2d p(x, li.lin.x() * x + li.lin.y());
|
||||
const double ti = along(li, p), tj = along(lj, p);
|
||||
if (ti > -eps && ti < length(li) + eps && tj > -eps && tj < length(lj) + eps)
|
||||
hits.push_back({ p, order[oi], order[oj] });
|
||||
}
|
||||
}
|
||||
std::sort(hits.begin(), hits.end(), [](const Hit &l, const Hit &r) { return l.p.x() < r.p.x(); });
|
||||
junctions.clear();
|
||||
for (const Hit &hit : hits) {
|
||||
int found = -1;
|
||||
for (int k = int(junctions.size()) - 1; k >= 0 && junctions[k].p.x() > hit.p.x() - eps; --k)
|
||||
if (std::abs(junctions[k].p.y() - hit.p.y()) < eps) {
|
||||
found = k;
|
||||
break;
|
||||
}
|
||||
if (found < 0) {
|
||||
found = int(junctions.size());
|
||||
junctions.push_back({ hit.p, {}, {}, {} });
|
||||
}
|
||||
std::vector<int> &jl = junctions[found].lines;
|
||||
for (int li : { hit.i, hit.j })
|
||||
if (std::find(jl.begin(), jl.end(), li) == jl.end())
|
||||
jl.push_back(li);
|
||||
}
|
||||
for (SweepLine &l : lines)
|
||||
l.junctions.clear();
|
||||
for (int ji = 0; ji < int(junctions.size()); ++ji)
|
||||
for (int li : junctions[ji].lines)
|
||||
lines[li].junctions.emplace_back(junctions[ji].p.x(), ji);
|
||||
for (SweepLine &l : lines)
|
||||
std::sort(l.junctions.begin(), l.junctions.end());
|
||||
};
|
||||
detect_junctions();
|
||||
auto has_arm = [&](int ji, int li, bool right) {
|
||||
const double t = along(lines[li], junctions[ji].p);
|
||||
return right ? t < length(lines[li]) - eps : t > eps;
|
||||
};
|
||||
|
||||
// A line ending on another just past a crossing stops at the crossing, where its stub would leave a hole.
|
||||
auto crosses = [&](int ji, int li) { return has_arm(ji, li, false) && has_arm(ji, li, true); };
|
||||
for (int pass = 0; pass < 3; ++pass) {
|
||||
std::vector<bool> touched(lines.size(), false);
|
||||
bool changed = false;
|
||||
for (int ji = 0; ji < int(junctions.size()); ++ji) {
|
||||
const Junction &J = junctions[ji];
|
||||
if (J.lines.size() != 2 || touched[J.lines[0]] || touched[J.lines[1]] || !crosses(ji, J.lines[0]) || !crosses(ji, J.lines[1]))
|
||||
continue;
|
||||
// Shortest arm of each line from J to the junction where it ends on another line.
|
||||
struct DeadArm { double length; bool at_b; int end; };
|
||||
std::array<DeadArm, 2> dead;
|
||||
dead.fill({ std::numeric_limits<double>::max(), false, -1 });
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
const SweepLine &l = lines[J.lines[k]];
|
||||
const size_t at = std::find_if(l.junctions.begin(), l.junctions.end(), [ji](const std::pair<double, int> &j) { return j.second == ji; }) - l.junctions.begin();
|
||||
const double t = along(l, J.p);
|
||||
if (at + 1 < l.junctions.size() && length(l) - along(l, junctions[l.junctions[at + 1].second].p) < eps)
|
||||
dead[k] = { length(l) - t, true, l.junctions[at + 1].second };
|
||||
if (at > 0 && along(l, junctions[l.junctions[at - 1].second].p) < eps && t < dead[k].length)
|
||||
dead[k] = { t, false, l.junctions[at - 1].second };
|
||||
}
|
||||
const int k = dead[0].length <= dead[1].length ? 0 : 1;
|
||||
if (dead[k].length >= 2. * d1)
|
||||
continue;
|
||||
SweepLine &l = lines[J.lines[k]];
|
||||
(dead[k].at_b ? l.b : l.a) = J.p;
|
||||
// Only the shortened line and those it ended on have stale junctions until the next pass.
|
||||
for (int li : junctions[dead[k].end].lines)
|
||||
touched[li] = true;
|
||||
changed = true;
|
||||
}
|
||||
if (!changed)
|
||||
break;
|
||||
detect_junctions();
|
||||
}
|
||||
|
||||
// At every crossing the lines bounce off each other, so that every path keeps running left to right.
|
||||
for (int ji = 0; ji < int(junctions.size()); ++ji) {
|
||||
Junction &J = junctions[ji];
|
||||
std::vector<int> left, right;
|
||||
for (int li : J.lines)
|
||||
if (has_arm(ji, li, false) && has_arm(ji, li, true))
|
||||
left.push_back(li);
|
||||
right = left;
|
||||
std::sort(left.begin(), left.end(), [&lines](int l, int r) { return lines[l].lin.x() > lines[r].lin.x(); });
|
||||
std::sort(right.begin(), right.end(), [&lines](int l, int r) { return lines[l].lin.x() < lines[r].lin.x(); });
|
||||
for (size_t k = 0; k < left.size(); ++k)
|
||||
J.pairs.emplace_back(left[k], right[k]);
|
||||
J.bends.assign(J.pairs.size(), { -1, -1 });
|
||||
}
|
||||
|
||||
std::vector<LevelPath> paths;
|
||||
for (int li = 0; li < int(lines.size()); ++li) {
|
||||
const SweepLine &l = lines[li];
|
||||
const int start = !l.junctions.empty() && !has_arm(l.junctions.front().second, li, false) ? l.junctions.front().second : -1;
|
||||
LevelPath path;
|
||||
path.start_term = start;
|
||||
path.verts.push_back(start >= 0 ? junctions[start].p : l.a);
|
||||
path.lines.push_back(li);
|
||||
int cur = li;
|
||||
double x = path.verts.front().x();
|
||||
for (;;) {
|
||||
const auto &js = lines[cur].junctions;
|
||||
const auto it = std::find_if(js.begin(), js.end(), [x, eps](const std::pair<double, int> &j) { return j.first > x + 0.25 * eps; });
|
||||
if (it == js.end()) {
|
||||
path.verts.push_back(lines[cur].b);
|
||||
break;
|
||||
}
|
||||
Junction &J = junctions[it->second];
|
||||
const size_t k = std::find_if(J.pairs.begin(), J.pairs.end(), [cur](const std::pair<int, int> &p) { return p.first == cur; }) - J.pairs.begin();
|
||||
if (k == J.pairs.size()) {
|
||||
path.verts.push_back(J.p);
|
||||
path.end_term = it->second;
|
||||
break;
|
||||
}
|
||||
if (const int next = J.pairs[k].second; next != cur) {
|
||||
J.bends[k] = { int(paths.size()), int(path.junctions.size()) };
|
||||
path.verts.push_back(J.p);
|
||||
path.lines.push_back(next);
|
||||
path.junctions.push_back(it->second);
|
||||
path.turn.push_back(lines[next].lin.x() > lines[cur].lin.x() ? 1 : -1);
|
||||
cur = next;
|
||||
}
|
||||
x = it->first;
|
||||
}
|
||||
path.cuts.resize(path.junctions.size());
|
||||
paths.push_back(std::move(path));
|
||||
}
|
||||
|
||||
// Cut the two bends of a crossing d1 apart, no further than the neighbouring bends turning the other way.
|
||||
for (const Junction &J : junctions) {
|
||||
if (J.pairs.size() < 2 || J.bends.front().first < 0 || J.bends.back().first < 0)
|
||||
continue;
|
||||
const Vec2d n = (family_dir[lines[J.pairs.back().second].family] - family_dir[lines[J.pairs.back().first].family]).normalized();
|
||||
auto cut = [&J, &n](double offset) {
|
||||
const Vec2d q = J.p + offset * n;
|
||||
const double s = -n.x() / n.y();
|
||||
return Lin(s, q.y() - s * q.x());
|
||||
};
|
||||
LevelPath &lo = paths[J.bends.front().first], &hi = paths[J.bends.back().first];
|
||||
const int lb = J.bends.front().second, hb = J.bends.back().second;
|
||||
if (J.pairs.size() == 3) {
|
||||
lo.cuts[lb].push_back(cut(-d1));
|
||||
hi.cuts[hb].push_back(cut(d1));
|
||||
continue;
|
||||
}
|
||||
// Only the tip of a small triangle, between the two slanted families, stops at its neighbouring bends.
|
||||
const bool sharp = lines[J.pairs.front().first].family != 0 && lines[J.pairs.front().second].family != 0;
|
||||
auto room = [&](const LevelPath &P, int b, int away, double sign) {
|
||||
double c = std::numeric_limits<double>::max();
|
||||
for (int nb : { b - 1, b + 1 })
|
||||
if (sharp && nb >= 0 && nb < int(P.junctions.size()) && P.turn[nb] == away)
|
||||
c = std::min(c, sign * n.dot(junctions[P.junctions[nb]].p - J.p));
|
||||
if (b == 0 && P.start_term >= 0)
|
||||
c = std::min(c, sign * n.dot(P.verts.front() - J.p));
|
||||
if (b + 1 == int(P.junctions.size()) && P.end_term >= 0)
|
||||
c = std::min(c, sign * n.dot(P.verts.back() - J.p));
|
||||
return std::max(c, 0.);
|
||||
};
|
||||
const double c_lo = room(lo, lb, 1, -1.), c_hi = room(hi, hb, -1, 1.);
|
||||
double d_lo = 0.5 * d1;
|
||||
if (d1 - c_hi <= c_lo)
|
||||
d_lo = std::clamp(d_lo, d1 - c_hi, c_lo);
|
||||
else if (c_lo + c_hi > 0.)
|
||||
d_lo = d1 * c_lo / (c_lo + c_hi);
|
||||
const double d_hi = d1 - d_lo;
|
||||
lo.cuts[lb].push_back(cut(-d_lo));
|
||||
hi.cuts[hb].push_back(cut(d_hi));
|
||||
auto propagate = [&](const LevelPath &P, int b, int away, int step, double offset) {
|
||||
for (int nb : { b - 1, b + 1 })
|
||||
if (nb >= 0 && nb < int(P.junctions.size()) && P.turn[nb] == away) {
|
||||
const Junction &W = junctions[P.junctions[nb]];
|
||||
const int k = int(std::find_if(W.pairs.begin(), W.pairs.end(), [&](const std::pair<int, int> &p) { return p.first == P.lines[nb]; }) - W.pairs.begin()) + step;
|
||||
if (k >= 0 && k < int(W.bends.size()) && W.bends[k].first >= 0)
|
||||
paths[W.bends[k].first].pushes.emplace_back(cut(offset), W.bends[k].second);
|
||||
}
|
||||
};
|
||||
if (sharp) {
|
||||
propagate(lo, lb, 1, -1, -d_lo - d1);
|
||||
propagate(hi, hb, -1, 1, d_hi + d1);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<Vec2d>> geometry;
|
||||
Linesf segments;
|
||||
std::vector<std::pair<int, double>> segment_start; // path, distance along it
|
||||
std::vector<std::pair<double, double>> kept; // stretch of each path left by the trimming
|
||||
for (const LevelPath &path : paths) {
|
||||
geometry.push_back(path.junctions.empty() ? std::vector<Vec2d>{ path.verts.front(), path.verts.back() } : path_points(path, lines, 4. * d1));
|
||||
double along_path = 0.;
|
||||
for (size_t i = 1; i < geometry.back().size(); ++i) {
|
||||
segments.emplace_back(geometry.back()[i - 1], geometry.back()[i]);
|
||||
segment_start.emplace_back(int(geometry.size()) - 1, along_path);
|
||||
along_path += (geometry.back()[i] - geometry.back()[i - 1]).norm();
|
||||
}
|
||||
kept.emplace_back(0., along_path);
|
||||
}
|
||||
|
||||
// A path ending on another line stops end_overlap inside the walls of the others, as trimmed so far.
|
||||
const double end_clearance = d1 - end_overlap;
|
||||
AABBTreeLines::LinesDistancer<Linef> tree(segments);
|
||||
auto clearance = [&](int pi, const Vec2d &q) {
|
||||
double dist = std::numeric_limits<double>::max();
|
||||
for (size_t s : tree.all_lines_in_radius(q, d1)) {
|
||||
const auto [pj, start] = segment_start[s];
|
||||
const Vec2d a = segments[s].a, d = segments[s].b - a;
|
||||
const double len = d.norm(), t0 = std::max(0., kept[pj].first - start), t1 = std::min(len, kept[pj].second - start);
|
||||
if (pj != pi && len > 0. && t0 <= t1)
|
||||
dist = std::min(dist, line_alg::distance_to(Linef(a + t0 / len * d, a + t1 / len * d), q));
|
||||
}
|
||||
return dist;
|
||||
};
|
||||
// Returns the length trimmed off.
|
||||
auto trim_front = [&](int pi, std::vector<Vec2d> &pts) {
|
||||
const double total = polyline_length(pts), step = d1 / 32.;
|
||||
double t = 0.;
|
||||
while (t <= total && clearance(pi, point_along(pts, t).first) < end_clearance)
|
||||
t += step;
|
||||
if (t > total) {
|
||||
pts.clear();
|
||||
return total;
|
||||
}
|
||||
if (t == 0.)
|
||||
return 0.;
|
||||
for (double lo = std::max(0., t - step); t - lo > step / 256.;)
|
||||
if (const double mid = 0.5 * (lo + t); clearance(pi, point_along(pts, mid).first) < end_clearance)
|
||||
lo = mid;
|
||||
else
|
||||
t = mid;
|
||||
const auto [q, seg] = point_along(pts, t);
|
||||
pts.erase(pts.begin(), pts.begin() + (seg - 1));
|
||||
pts.front() = q;
|
||||
return t;
|
||||
};
|
||||
|
||||
// A path stopping on the line of another path is trimmed first, so that it gives way to that path.
|
||||
std::vector<std::vector<std::tuple<double, double, int>>> carried(lines.size()); // x range and path of each piece
|
||||
for (int pi = 0; pi < int(paths.size()); ++pi)
|
||||
for (size_t i = 0; i < paths[pi].lines.size(); ++i)
|
||||
carried[paths[pi].lines[i]].emplace_back(paths[pi].verts[i].x(), paths[pi].verts[i + 1].x(), pi);
|
||||
std::vector<std::vector<int>> stopping_on(paths.size());
|
||||
for (int pi = 0; pi < int(paths.size()); ++pi)
|
||||
for (const auto &[ji, own] : { std::make_pair(paths[pi].start_term, paths[pi].lines.front()), std::make_pair(paths[pi].end_term, paths[pi].lines.back()) })
|
||||
if (ji >= 0)
|
||||
for (int li : junctions[ji].lines)
|
||||
if (li != own)
|
||||
for (const auto &[x0, x1, pj] : carried[li])
|
||||
if (pj != pi && x0 - eps <= junctions[ji].p.x() && junctions[ji].p.x() <= x1 + eps)
|
||||
stopping_on[pj].push_back(pi);
|
||||
std::vector<int> order;
|
||||
std::vector<bool> visited(paths.size(), false);
|
||||
std::function<void(int)> visit = [&](int pi) {
|
||||
if (visited[pi])
|
||||
return;
|
||||
visited[pi] = true;
|
||||
for (int child : stopping_on[pi])
|
||||
visit(child);
|
||||
order.push_back(pi);
|
||||
};
|
||||
for (int pi = 0; pi < int(paths.size()); ++pi)
|
||||
visit(pi);
|
||||
std::vector<std::vector<Vec2d>> trimmed(paths.size());
|
||||
auto trim = [&](int pi) {
|
||||
std::vector<Vec2d> &pts = trimmed[pi];
|
||||
if (paths[pi].start_term >= 0)
|
||||
kept[pi].first += trim_front(pi, pts);
|
||||
if (paths[pi].end_term >= 0 && !pts.empty()) {
|
||||
std::reverse(pts.begin(), pts.end());
|
||||
kept[pi].second -= trim_front(pi, pts);
|
||||
std::reverse(pts.begin(), pts.end());
|
||||
}
|
||||
if (pts.size() < 2 || polyline_length(pts) < d1) {
|
||||
pts.clear();
|
||||
kept[pi] = { 0., -1. };
|
||||
}
|
||||
};
|
||||
// Ends grow back where the ends they gave way to were trimmed later; the last pass only shortens them.
|
||||
for (int pass = 0; pass < 3; ++pass)
|
||||
for (int pi : order) {
|
||||
if (pass < 2) {
|
||||
trimmed[pi] = geometry[pi];
|
||||
kept[pi] = { 0., polyline_length(geometry[pi]) };
|
||||
} else if (trimmed[pi].empty())
|
||||
continue;
|
||||
trim(pi);
|
||||
}
|
||||
|
||||
Polylines out;
|
||||
const Eigen::Rotation2Dd to_world = to_sweep.inverse();
|
||||
for (const std::vector<Vec2d> &pts : trimmed) {
|
||||
if (pts.empty())
|
||||
continue;
|
||||
Polyline pl;
|
||||
for (const Vec2d &p : pts) {
|
||||
const Vec2d w = to_world * p;
|
||||
pl.points.emplace_back(coord_t(std::round(w.x())), coord_t(std::round(w.y())));
|
||||
}
|
||||
out.emplace_back(std::move(pl));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void Filler::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
@@ -1371,6 +1932,17 @@ void Filler::_fill_surface_single(
|
||||
all_polylines.reserve(lines.size());
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
|
||||
if (params.multiline > 1) {
|
||||
const double d1 = scale_(this->spacing) * params.multiline;
|
||||
BoundingBox cover = get_extents(expolygon);
|
||||
cover.offset(coord_t(4. * d1));
|
||||
// Rotate the family the paths run along with the layer, like the other multiline patterns.
|
||||
const int sweep = int((this->layer_id / std::max(thickness_layers, 1u)) % 3);
|
||||
// Line ends overlap the walls they stop on by half a line, so that they bond.
|
||||
if (Polylines paths = multiline_paths(lines, d1, 0.5 * scale_(this->spacing), sweep, cover); !paths.empty())
|
||||
all_polylines = std::move(paths);
|
||||
}
|
||||
|
||||
// Apply multiline offset if needed
|
||||
multiline_fill(all_polylines, params, spacing);
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ FillAdaptive::OctreePtr build_octree(
|
||||
// If true, octree is densified below internal overhangs only.
|
||||
bool support_overhangs_only);
|
||||
|
||||
// Multiline infill: lines of the three families to non-crossing paths d1 apart, ends reaching end_overlap into walls.
|
||||
Polylines multiline_paths(const Lines &lines, double d1, double end_overlap, int sweep, const BoundingBox &cover);
|
||||
|
||||
//
|
||||
// Some of the algorithms used by class FillAdaptive were inspired by
|
||||
// Cura Engine's class SubDivCube
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user