mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
Sync
This commit is contained in:
parent
2368445378
commit
c384911f7c
33 changed files with 1322 additions and 426 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -60,7 +60,10 @@ firmware/
|
|||
# Node modules
|
||||
node_modules/
|
||||
|
||||
data/
|
||||
# Runtime data dir (db, archives, backups). Anchored to the repo root on
|
||||
# purpose: a bare `data/` also matches frontend/src/data and
|
||||
# backend/app/data, which are source, not runtime state.
|
||||
/data/
|
||||
|
||||
# Local-dev runtime caches (matplotlib MPLCONFIGDIR lands here when DATA_DIR
|
||||
# is unset, so base_dir resolves to the repo root). In Docker this sits
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ All notable changes to Bambuddy will be documented in this file.
|
|||
- **Temperatures on the streaming overlay, and a builder for its URL (#1422, reporter @SMAW)** — The overlay at `/overlay/{printer}` draws live print data over a full-screen camera view for OBS, a wall display or any browser source. It could already be tuned — which fields, what size, what frame rate — but only through query parameters documented in the wiki, and temperatures were not among the fields on offer. Both are now addressed. Nozzle, bed and chamber readings join the list, shown with the target while the heater is still climbing and with the target dropped once it is reached, so a settled hotend reads "220°C" rather than "220 / 220°C" for the rest of the print. Both nozzles appear on a dual-nozzle printer. They are drawn whether or not a print is running, since a preheating machine is exactly when they are worth watching, and each reading appears only when the printer genuinely reports it — chamber temperature stays absent on P1 and A1 models, which publish a value with no sensor behind it. And **Settings → API Keys → Streaming Overlay** now builds the URL for you: pick the printer, tick the fields, set size and frame rate, paste in a token if login is enabled, and copy the result, with an optional preview alongside it. The preview stays off until you ask for it so that leaving the settings page open does not hold a viewer on the printer's single camera connection. Making that preview possible needed one narrow change to the security headers: the overlay path now sends `frame-ancestors 'self'` instead of `'none'`, so Bambuddy's own UI can embed it. Every other page still refuses to be framed at all, `'self'` permits a framer only on this same origin, and embedding the overlay from another host — Home Assistant on a different port, say — is unchanged and still requires `TRUSTED_FRAME_ORIGINS`. Temperatures are not in the default field set, so an overlay URL already pasted into a scene looks exactly the same after upgrading. Translated in all locales, wiki updated, covered by backend and frontend tests.
|
||||
- **The external spool can be hidden from the printer card (#1782, reporter @Arn0uDz)** — An external spool holder that never gets used still occupies a full card's width in the **Filaments** row, next to the AMS units that are actually being used. An eye icon at the right-hand end of that row's header now hides it, and clicking it again brings it back, so nothing is lost behind a settings page you would have to remember. The choice is remembered per printer and stored in the browser, like the card size and the offline-printer filter — one machine in a fleet can be tidied up without touching the others, and nothing changes for anyone else using the same Bambuddy. The icon is deliberately absent on a printer with no AMS: there the external spool is the entire filament section, and hiding it would leave an empty row. That guard also covers the case of an AMS being unplugged from a printer whose external spool was hidden earlier — the spool reappears rather than leaving a blank row behind. On the H2D and H2S both external positions share one card and so hide together. Translated in all locales, wiki updated, covered by frontend tests.
|
||||
|
||||
- **The slice dialog can edit the full print-parameter set, not just pick a preset** — Slicing from Bambuddy meant taking a process preset exactly as it came. Anything beyond that — one more wall for a bracket, supports for a single overhang, slower outer walls on a part that keeps scarring — meant going back to Bambu Studio, editing there, and re-exporting. The slice dialog now has a **Process settings** section carrying the whole tree: the same pages, groups and ordering the desktop slicer shows under Print Settings, with the same labels, tooltips, ranges and defaults, because they are extracted from the slicer's own sources rather than hand-picked. The dialog itself widens to make room: on a reasonably sized screen it now uses two columns, with every "what am I slicing with" decision — pipeline, printer, process, filaments, bed type, layout passes — kept together on the left and the settings panel given a column of its own on the right, open and ready rather than folded away. Narrower screens keep the single column and the collapsed panel. It behaves the way the desktop one does. **Simple / Advanced / Expert** matches the slicer's own visibility tiers, search reaches across every page at once, changed settings are marked and individually revertable, and settings the slicer itself disables in your current configuration are greyed out — infill options with infill at zero, ironing options with ironing off — because Bambuddy evaluates the slicer's own enable rules rather than approximating them. Where a rule cannot be decided with certainty the setting stays editable, on the grounds that a missing control looks like a bug while a redundant one is merely ignored. Edits apply to one slice, are not saved into a preset, and are written after the source file's support configuration and any carried designer settings, so an explicit choice is never silently overridden; an untouched panel produces exactly the request it did before. Parameter names and descriptions are in English even where the rest of Bambuddy is not — several hundred strings lifted verbatim from the slicer, which is a separate job from translating Bambuddy's own interface. The dialog's own wording is translated in all locales. Wiki updated, covered by backend and frontend tests.
|
||||
- **The slice dialog can edit the full print-parameter set, not just pick a preset** — Slicing from Bambuddy meant taking a process preset exactly as it came. Anything beyond that — one more wall for a bracket, supports for a single overhang, slower outer walls on a part that keeps scarring — meant going back to Bambu Studio, editing there, and re-exporting. The slice dialog now has a **Process settings** section carrying the whole tree: the same pages, groups and ordering the desktop slicer shows under Print Settings, with the same labels, tooltips, ranges and defaults, because they are extracted from the slicer's own sources rather than hand-picked. The dialog itself widens to make room: on a reasonably sized screen it now uses two columns, with every "what am I slicing with" decision — pipeline, printer, process, filaments, bed type, layout passes — kept together on the left and the settings panel given a column of its own on the right, open and ready rather than folded away. Narrower screens keep the single column and the collapsed panel. The settings a source file's designer changed (#2622) now live in this panel too, marked *from file* against the options they belong to instead of in a separate list further up the dialog -- so there is one place that shows what a slice will actually use. Machine-coupled ones stay flagged and unticked as before, anything the panel has no entry for is listed by name rather than quietly dropped, and typing your own value still wins. Switching on "Use the file's built-in settings" greys the panel out rather than removing it, so the dialog does not appear to lose a feature when that toggle is flipped -- it stays visible, says why it is inactive, and applies nothing. Options that select *which* filament prints a feature -- support base and interface, and the per-region pickers for walls, infill and surfaces -- list the filaments you actually picked on the left rather than asking for a slot number, so "support interface" can be set to the PVA in slot 2 by name. Defaults and ranges are read out of the slicer's C++ initialisers, so a few arrived in source form -- the whole Line width group showed "0." rather than "0" -- and those are now cleaned as the data is generated instead of being papered over at display time. It behaves the way the desktop one does. **Simple / Advanced / Expert** matches the slicer's own visibility tiers, search reaches across every page at once, changed settings are marked and individually revertable, and settings the slicer itself disables in your current configuration are greyed out — infill options with infill at zero, ironing options with ironing off — because Bambuddy evaluates the slicer's own enable rules rather than approximating them. Where a rule cannot be decided with certainty the setting stays editable, on the grounds that a missing control looks like a bug while a redundant one is merely ignored. Edits apply to one slice, are not saved into a preset, and are written after the source file's support configuration and any carried designer settings, so an explicit choice is never silently overridden; an untouched panel produces exactly the request it did before. Parameter names and descriptions are in English even where the rest of Bambuddy is not — several hundred strings lifted verbatim from the slicer, which is a separate job from translating Bambuddy's own interface. The dialog's own wording is translated in all locales. Wiki updated, covered by backend and frontend tests.
|
||||
|
||||
### Changed
|
||||
- **The slice dialog's process and filament lists now leave out presets that belong to another printer** — They were already sorted by compatibility, but a preset for a different Bambu model still appeared, demoted to an "Other printers" group at the bottom of the dropdown. With a large cloud filament library that group is most of the list, so the filtering was doing little for the thing it was meant to help: finding the profile you actually want. Those presets are now held back, with the label reporting how many ("3 hidden") next to a **Show all** link that brings them back for that one dropdown. Two things are never hidden. A preset with no detectable printer — a custom or renamed profile — stays in the list, because absence of evidence is not evidence of incompatibility and hiding those would make people's own imported profiles vanish. And whatever is currently selected stays visible even when the list is collapsed, so a deliberate cross-printer pick, or one restored from a pipeline, is never silently discarded by being dropped from the options. Re-slicing for another printer remains fully supported, so this is a default view rather than a restriction. Fixing this also corrected a screen-reader bug in those dropdowns: the controls sat inside the label wrapping the select, which handed them the entire label as their spoken name. A separate defect surfaced alongside it — the filter did nothing at all when the selected printer was a preset you had edited, because BambuStudio names those copies with a leading "# " and the matcher did not know to look past it. On such a printer every preset read as "compatibility unknown", and profiles listing their compatible printers by name could be ruled out against the very printer they were cloned from.
|
||||
- **The MQTT debug log now records the commands sent to a printer, not only what it reports back** — **Printer → Debug → MQTT** captured one side of the conversation. Bambuddy listens on both of a printer's topics, but the one carrying commands returned before anything was written to the log, so a capture could show every status push the printer made and nothing it was ever told — including the commands Bambu Studio sends over the local network, which is the only place they can be observed at all. Those now appear alongside Bambuddy's own, grouped under the outgoing filter. It is what lets a question like "which value does Studio put in this field?" be answered from a user's capture instead of guessed at, and it is why #2774 could not be taken further. Commands Bambuddy sends appear twice, once as it publishes and once as the broker echoes it back, and the pair is itself evidence the command reached the broker. Logging is off until switched on, as before. Covered by backend tests.
|
||||
- **The L and XL printer cards now scale their text and icons, not just their width (#1848, reporter @misterff1)** — Switching a card from M to XL made it wider, enlarged the printer name and the thumbnail, and left everything else exactly as it was: the AMS slot labels, temperatures, filament names, status text and every small button stayed pinned between 8 and 11 pixels, well under the smallest size used anywhere else in Bambuddy. The result was a full-width card carrying the same tiny text as the compact one, which is precisely the opposite of what someone reaching for a bigger card is asking for. Browser zoom is not an answer to this, since it enlarges the entire page and so preserves the very disparity being complained about. The card body now scales along with the card: L draws it 20% larger and XL 40% larger, icons included, so the controls grow with the text rather than staying fiddly to hit. The AMS-HT card needed two adjustments of its own, since its temperature and humidity readings sit beside the slot rather than under it. Its single slot was the only thing on that row able to grow, so it swallowed every spare pixel and pushed the readings hard against the card's edge — it is now capped at roughly two ordinary slots, which keeps them clear at any card width. The card itself also gained a ceiling of one full AMS card's width, so a unit that wraps onto a line of its own no longer stretches that single slot across the whole card. S and M are deliberately untouched — S is the dense fleet view where density is the point, and M is the default, so an existing install looks identical until you reach for a size that is already asking for more room. Wiki updated. Covered by frontend tests.
|
||||
- **Error and warning toasts now stay up twice as long** — Every pop-up notification disappeared after three seconds regardless of what it said. That is about right for "Settings saved", which confirms something you just did and is skimmed rather than read, but errors and warnings are a different kind of message: they carry a reason, often one relayed from the printer or the backend, and they run to a couple of lines. Three seconds was not long enough to finish reading one, and a missed error message is gone for good — there is no notification history to go back to. Errors and warnings now hold for six seconds. Success and informational toasts keep the three-second default, so the common case of clicking something and seeing it confirmed is unchanged, and the close button and the manual dismiss work exactly as before on all of them. The background print-dispatch toast is unaffected: it stays up while it has work in progress and clears itself shortly after the last job settles. Covered by frontend tests.
|
||||
|
|
|
|||
|
|
@ -80,11 +80,37 @@ for (const k of CONDITION_KEYS) if (schema[k]) referenced.add(k);
|
|||
// options, most of it source-location bookkeeping we have no use for.
|
||||
const KEEP = ['type', 'mode', 'label', 'tooltip', 'sidetext', 'min', 'max', 'enum_values', 'enum_labels', 'default'];
|
||||
|
||||
// The extractor reads defaults and bounds straight out of C++ initialisers, so
|
||||
// float literals arrive in source form: `0.` stays "0.", `0.3f` stays "0.3f",
|
||||
// `100.%` stays "100.%", and `0.f` even splits into [0, "f"]. Rendering those
|
||||
// verbatim put a column of "0." in the Line width group. They are literal
|
||||
// artefacts, not values, so they are cleaned here — once, in the data — rather
|
||||
// than worked around in every place that displays a default.
|
||||
function normaliseLiteral(value) {
|
||||
if (Array.isArray(value)) {
|
||||
// `0.f` split across two entries; the stray "f" is not a value.
|
||||
const cleaned = value.filter((v) => v !== 'f').map(normaliseLiteral);
|
||||
return cleaned.length > 0 ? cleaned : [0];
|
||||
}
|
||||
if (typeof value !== 'string') return value;
|
||||
|
||||
let s = value.trim();
|
||||
s = s.replace(/^(-?[\d.]+)f$/, '$1'); // 0.3f -> 0.3, 0.f -> 0.
|
||||
s = s.replace(/^(-?[\d.]*)\.%$/, '$1%'); // 100.% -> 100%
|
||||
s = s.replace(/^(-?[\d.]*)\.$/, '$1'); // 0. -> 0
|
||||
// A literal that was nothing but a dot carried no digits to keep.
|
||||
if (s === '' || s === '-') return value;
|
||||
return s;
|
||||
}
|
||||
|
||||
const trimmedSchema = {};
|
||||
for (const key of [...referenced].sort()) {
|
||||
const opt = schema[key];
|
||||
const out = {};
|
||||
for (const f of KEEP) if (opt[f] !== undefined) out[f] = opt[f];
|
||||
for (const f of KEEP) {
|
||||
if (opt[f] === undefined) continue;
|
||||
out[f] = f === 'default' || f === 'min' || f === 'max' ? normaliseLiteral(opt[f]) : opt[f];
|
||||
}
|
||||
trimmedSchema[key] = out;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ vi.mock('../../api/client', () => ({
|
|||
// Slicer Pipelines (#1425)
|
||||
listSlicerPipelines: vi.fn(),
|
||||
createSlicerPipeline: vi.fn(),
|
||||
getSlicerPrinterModels: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
@ -47,6 +48,7 @@ const mockApi = api as unknown as {
|
|||
getArchiveFilamentRequirements: ReturnType<typeof vi.fn>;
|
||||
listSlicerPipelines: ReturnType<typeof vi.fn>;
|
||||
createSlicerPipeline: ReturnType<typeof vi.fn>;
|
||||
getSlicerPrinterModels: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
function makeUnified(overrides: Partial<UnifiedPresetsResponse> = {}): UnifiedPresetsResponse {
|
||||
|
|
@ -350,12 +352,27 @@ describe('SliceModal', () => {
|
|||
],
|
||||
};
|
||||
|
||||
// The designer's settings are shown inside the process-settings panel now,
|
||||
// against the options they belong to, rather than in a list of their own.
|
||||
// The payload contract below is unchanged: their *values* still travel as
|
||||
// design_overrides keys, read from the file by the backend.
|
||||
async function openDesignSection() {
|
||||
const user = userEvent.setup();
|
||||
await user.click(await screen.findByText(/Keep the designer's settings/));
|
||||
await user.click(await screen.findByRole('button', { name: /Process settings/ }));
|
||||
await screen.findByPlaceholderText('Search settings');
|
||||
// Every designer key must be reachable, including expert-tier ones.
|
||||
await user.click(screen.getByRole('button', { name: 'Expert' }));
|
||||
return user;
|
||||
}
|
||||
|
||||
/** The panel's per-option "use the file's value" checkbox, by option key. */
|
||||
function sourceCheckbox(key: string): HTMLInputElement {
|
||||
const boxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
|
||||
const found = boxes.find((b) => (b.getAttribute('aria-label') ?? '').includes(key));
|
||||
if (!found) throw new Error(`no source checkbox for ${key}`);
|
||||
return found;
|
||||
}
|
||||
|
||||
it("carries the design's printer-independent settings by default (#2622)", async () => {
|
||||
mockApi.sliceLibraryFile.mockResolvedValue({
|
||||
job_id: 42,
|
||||
|
|
@ -369,8 +386,9 @@ describe('SliceModal', () => {
|
|||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
// Two of three pre-selected: the speed key is machine-coupled.
|
||||
expect(await screen.findByText('2 of 3 selected')).toBeInTheDocument();
|
||||
// Two of three pre-selected: the speed key is machine-coupled and is
|
||||
// offered but never pre-ticked.
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: /^Slice$/ })).toBeEnabled());
|
||||
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
|
@ -388,15 +406,18 @@ describe('SliceModal', () => {
|
|||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
await openDesignSection();
|
||||
const user = await openDesignSection();
|
||||
|
||||
expect(screen.getByText('wall_loops')).toBeInTheDocument();
|
||||
expect(screen.getByText('5')).toBeInTheDocument();
|
||||
expect(screen.getByText('sparse_infill_density')).toBeInTheDocument();
|
||||
expect(screen.getByText('100%')).toBeInTheDocument();
|
||||
// The risky one is listed too — visible, explained, just not pre-ticked.
|
||||
expect(screen.getByText('outer_wall_speed')).toBeInTheDocument();
|
||||
expect(screen.getByText('printer-specific')).toBeInTheDocument();
|
||||
// Carried keys show the designer's value in the option's own control.
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'wall loops');
|
||||
await waitFor(() => expect(screen.getByLabelText(/^Wall loops/)).toHaveValue(5));
|
||||
expect(sourceCheckbox('Wall loops').checked).toBe(true);
|
||||
|
||||
await user.clear(screen.getByPlaceholderText('Search settings'));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'outer wall speed');
|
||||
// The machine-coupled one is present and flagged, just not pre-ticked.
|
||||
await waitFor(() => expect(screen.getAllByText("designer's printer").length).toBeGreaterThan(0));
|
||||
expect(sourceCheckbox('Outer wall').checked).toBe(false);
|
||||
});
|
||||
|
||||
it('lets the user opt a machine-coupled setting in and a safe one out (#2622)', async () => {
|
||||
|
|
@ -413,12 +434,15 @@ describe('SliceModal', () => {
|
|||
});
|
||||
|
||||
const user = await openDesignSection();
|
||||
const boxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
|
||||
const byKey = (key: string) =>
|
||||
boxes.find((b) => b.closest('label')?.textContent?.includes(key)) as HTMLInputElement;
|
||||
|
||||
await user.click(byKey('outer_wall_speed'));
|
||||
await user.click(byKey('wall_loops'));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'outer wall speed');
|
||||
await waitFor(() => expect(sourceCheckbox('Outer wall')).toBeInTheDocument());
|
||||
await user.click(sourceCheckbox('Outer wall'));
|
||||
|
||||
await user.clear(screen.getByPlaceholderText('Search settings'));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'wall loops');
|
||||
await waitFor(() => expect(sourceCheckbox('Wall loops')).toBeInTheDocument());
|
||||
await user.click(sourceCheckbox('Wall loops'));
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
||||
|
|
@ -441,9 +465,11 @@ describe('SliceModal', () => {
|
|||
});
|
||||
|
||||
const user = await openDesignSection();
|
||||
const boxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
|
||||
for (const box of boxes) {
|
||||
if (box.checked) await user.click(box);
|
||||
for (const key of ['Wall loops', 'Sparse infill density']) {
|
||||
await user.clear(screen.getByPlaceholderText('Search settings'));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), key.toLowerCase());
|
||||
await waitFor(() => expect(sourceCheckbox(key)).toBeInTheDocument());
|
||||
if (sourceCheckbox(key).checked) await user.click(sourceCheckbox(key));
|
||||
}
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
|
@ -461,7 +487,10 @@ describe('SliceModal', () => {
|
|||
});
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: /^Slice$/ })).toBeEnabled());
|
||||
expect(screen.queryByText(/Keep the designer's settings/)).toBeNull();
|
||||
// The panel still exists — it is the editor — but nothing is marked as
|
||||
// coming from the file.
|
||||
expect(screen.queryByText('from file')).toBeNull();
|
||||
expect(screen.queryByText("designer's printer")).toBeNull();
|
||||
});
|
||||
|
||||
it('includes bed_type in the request when the user picks a non-auto plate (#1337)', async () => {
|
||||
|
|
@ -1523,6 +1552,73 @@ describe('SliceModal', () => {
|
|||
* test setup pins matchMedia to `matches: false`, so every other test in this
|
||||
* file exercises the narrow single-stack path; these override it.
|
||||
*/
|
||||
describe('SliceModal — process settings in "slice as designed" mode', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockApi.getSlicerPresets.mockResolvedValue(fullThreeTier);
|
||||
mockApi.listSlicerPipelines.mockResolvedValue({ pipelines: [] });
|
||||
mockApi.getSlicerPrinterModels.mockResolvedValue({});
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue({
|
||||
file_id: 100,
|
||||
filename: 'Designed.3mf',
|
||||
plates: [],
|
||||
is_multi_plate: false,
|
||||
embedded_printer: 'Bambu Lab X1 Carbon 0.4 nozzle',
|
||||
embedded_process: '0.20mm Standard',
|
||||
});
|
||||
mockApi.getLibraryFileFilamentRequirements.mockResolvedValue({
|
||||
file_id: 100, filename: 'Designed.3mf', plate_id: 1, filaments: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('disables the panel rather than removing it', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
const toggle = (await screen.findByLabelText(/Use the file's built-in settings/)) as HTMLInputElement;
|
||||
const header = await screen.findByRole('button', { name: /Process settings/ });
|
||||
await user.click(header);
|
||||
const search = await screen.findByPlaceholderText('Search settings');
|
||||
expect(search).toBeEnabled();
|
||||
|
||||
await user.click(toggle);
|
||||
|
||||
// Still on screen — hiding it made the dialog look like it had lost a
|
||||
// feature — but nothing in it can be operated, because nothing in it is
|
||||
// sent on this path.
|
||||
expect(screen.getByPlaceholderText('Search settings')).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Expert' })).toBeDisabled();
|
||||
expect(screen.getByText(/Not used while/)).toBeInTheDocument();
|
||||
expect(screen.getByText('Inactive')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sends no process overrides once the file drives the slice', async () => {
|
||||
mockApi.sliceLibraryFile.mockResolvedValue({ job_id: 42, status: 'pending', status_url: '/x' });
|
||||
const user = userEvent.setup();
|
||||
renderWithTracker({
|
||||
source: { kind: 'libraryFile', id: 100, filename: 'Designed.3mf' },
|
||||
onClose: vi.fn(),
|
||||
});
|
||||
|
||||
// Edit something, then hand the slice over to the file's own settings.
|
||||
await user.click(await screen.findByRole('button', { name: /Process settings/ }));
|
||||
const input = await screen.findByLabelText(/^Layer height/);
|
||||
await user.clear(input);
|
||||
await user.type(input, '0.16');
|
||||
|
||||
await user.click(screen.getByLabelText(/Use the file's built-in settings/));
|
||||
await user.click(screen.getByRole('button', { name: /^Slice$/ }));
|
||||
|
||||
await waitFor(() => expect(mockApi.sliceLibraryFile).toHaveBeenCalled());
|
||||
const payload = mockApi.sliceLibraryFile.mock.calls[0][1] as Record<string, unknown>;
|
||||
expect(payload.use_embedded_settings).toBe(true);
|
||||
expect(payload).not.toHaveProperty('process_overrides');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SliceModal — process settings layout', () => {
|
||||
const setViewport = (wide: boolean) => {
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
|
|
@ -1583,6 +1679,111 @@ describe('SliceModal — process settings layout', () => {
|
|||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Process and filament lists hold back presets that resolve to a *different*
|
||||
* printer, behind a per-slot "Show all". Two things must never be hidden: a
|
||||
* preset whose compatibility is merely unknown, and whatever is currently
|
||||
* selected.
|
||||
*/
|
||||
describe('SliceModal — presets filtered by the selected printer', () => {
|
||||
const presets: UnifiedPresetsResponse = {
|
||||
cloud: { printer: [], process: [], filament: [] },
|
||||
orca_cloud: { printer: [], process: [], filament: [] },
|
||||
local: { printer: [], process: [], filament: [] },
|
||||
standard: {
|
||||
printer: [
|
||||
{ id: 'Bambu Lab X1 Carbon 0.4 nozzle', name: 'Bambu Lab X1 Carbon 0.4 nozzle', source: 'standard' },
|
||||
],
|
||||
process: [
|
||||
{ id: 'p-x1c', name: '0.20mm Standard @BBL X1C', source: 'standard' },
|
||||
{ id: 'p-h2d', name: '0.20mm Standard @BBL H2D', source: 'standard' },
|
||||
{ id: 'p-a1m', name: '0.20mm Standard @BBL A1M', source: 'standard' },
|
||||
// No printer tag at all — compatibility is unknown, never hidden.
|
||||
{ id: 'p-custom', name: 'My own profile', source: 'standard' },
|
||||
],
|
||||
filament: [{ id: 'f-x1c', name: 'Bambu PLA Basic @BBL X1C', source: 'standard' }],
|
||||
},
|
||||
cloud_status: 'ok',
|
||||
orca_cloud_status: 'ok',
|
||||
} as UnifiedPresetsResponse;
|
||||
|
||||
const processOptionNames = () =>
|
||||
Array.from(presetSelects()[1].options).map((o) => o.textContent);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockApi.getSlicerPresets.mockResolvedValue(presets);
|
||||
mockApi.getSlicerPrinterModels.mockResolvedValue({ 'Bambu Lab X1 Carbon': 'X1C' });
|
||||
mockApi.listSlicerPipelines.mockResolvedValue({ pipelines: [] });
|
||||
mockApi.getLibraryFilePlates.mockResolvedValue({
|
||||
file_id: 100, filename: 'Cube.stl', plates: [], is_multi_plate: false,
|
||||
});
|
||||
mockApi.getLibraryFileFilamentRequirements.mockResolvedValue({
|
||||
file_id: 100, filename: 'Cube.stl', plate_id: 1, filaments: [],
|
||||
});
|
||||
});
|
||||
|
||||
const open = async () => {
|
||||
renderWithTracker({ source: { kind: 'libraryFile', id: 100, filename: 'Cube.stl' }, onClose: vi.fn() });
|
||||
await waitFor(() => expect(presetSelects().length).toBeGreaterThan(1));
|
||||
};
|
||||
|
||||
it('leaves out presets belonging to another printer', async () => {
|
||||
await open();
|
||||
await waitFor(() => expect(processOptionNames()).toContain('0.20mm Standard @BBL X1C'));
|
||||
expect(processOptionNames()).not.toContain('0.20mm Standard @BBL H2D');
|
||||
expect(processOptionNames()).not.toContain('0.20mm Standard @BBL A1M');
|
||||
});
|
||||
|
||||
it('keeps a preset whose compatibility cannot be determined', async () => {
|
||||
await open();
|
||||
// An untagged preset carries no evidence either way; hiding it would make
|
||||
// a user's own imported profiles vanish.
|
||||
await waitFor(() => expect(processOptionNames()).toContain('My own profile'));
|
||||
});
|
||||
|
||||
it('says how many it held back and reveals them on request', async () => {
|
||||
const user = userEvent.setup();
|
||||
await open();
|
||||
|
||||
const hidden = await screen.findByText('2 hidden');
|
||||
expect(hidden).toBeInTheDocument();
|
||||
|
||||
await user.click(within(hidden.parentElement as HTMLElement).getByRole('button', { name: 'Show all' }));
|
||||
await waitFor(() => expect(processOptionNames()).toContain('0.20mm Standard @BBL H2D'));
|
||||
expect(processOptionNames()).toContain('0.20mm Standard @BBL A1M');
|
||||
});
|
||||
|
||||
it('collapses the list again on Show fewer', async () => {
|
||||
const user = userEvent.setup();
|
||||
await open();
|
||||
|
||||
await user.click((await screen.findAllByRole('button', { name: 'Show all' }))[0]);
|
||||
await waitFor(() => expect(processOptionNames()).toContain('0.20mm Standard @BBL H2D'));
|
||||
|
||||
await user.click(screen.getAllByRole('button', { name: 'Show fewer' })[0]);
|
||||
await waitFor(() => expect(processOptionNames()).not.toContain('0.20mm Standard @BBL H2D'));
|
||||
});
|
||||
|
||||
it('never hides the preset that is currently selected', async () => {
|
||||
const user = userEvent.setup();
|
||||
await open();
|
||||
|
||||
// Reach a cross-printer preset, pick it, then collapse the list again.
|
||||
await user.click((await screen.findAllByRole('button', { name: 'Show all' }))[0]);
|
||||
await waitFor(() => expect(processOptionNames()).toContain('0.20mm Standard @BBL H2D'));
|
||||
await user.selectOptions(presetSelects()[1], 'standard:p-h2d');
|
||||
await user.click(screen.getAllByRole('button', { name: 'Show fewer' })[0]);
|
||||
|
||||
// Dropping it from the options would blank the select and silently discard
|
||||
// a deliberate cross-printer choice.
|
||||
await waitFor(() => expect(processOptionNames()).toContain('0.20mm Standard @BBL H2D'));
|
||||
expect(presetSelects()[1].value).toBe('standard:p-h2d');
|
||||
// The one still-hidden preset is counted; the selected one is not.
|
||||
expect(screen.getByText('1 hidden')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('pickFilamentForSlot — printer-compat contract (#1851)', () => {
|
||||
// Index that recognises @BBL H2C / @BBL A1 tokens via the canonical
|
||||
// PRINTER_MODEL_MAP. Real production data comes through
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import userEvent from '@testing-library/user-event';
|
|||
import { useState } from 'react';
|
||||
|
||||
import { render } from '../utils';
|
||||
import SlicerSettingsPanel from '../../components/SlicerSettingsPanel';
|
||||
import SlicerSettingsPanel, { type FilamentChoice } from '../../components/SlicerSettingsPanel';
|
||||
import type { SettingValue } from '../../types/slicerSettings';
|
||||
import type { DesignOverride } from '../../types/plates';
|
||||
|
||||
/**
|
||||
* The panel is a controlled component: it renders from the `values` prop and
|
||||
|
|
@ -16,11 +17,18 @@ import type { SettingValue } from '../../types/slicerSettings';
|
|||
function Harness({
|
||||
initial,
|
||||
onChange,
|
||||
sourceOverrides,
|
||||
initialSelected,
|
||||
filamentChoices,
|
||||
}: {
|
||||
initial: Record<string, SettingValue>;
|
||||
onChange: (v: Record<string, SettingValue>, s: Record<string, string | string[]>) => void;
|
||||
sourceOverrides?: DesignOverride[];
|
||||
initialSelected?: string[];
|
||||
filamentChoices?: FilamentChoice[];
|
||||
}) {
|
||||
const [values, setValues] = useState(initial);
|
||||
const [selected, setSelected] = useState(new Set(initialSelected ?? []));
|
||||
return (
|
||||
<SlicerSettingsPanel
|
||||
values={values}
|
||||
|
|
@ -28,14 +36,32 @@ function Harness({
|
|||
setValues(v);
|
||||
onChange(v, s);
|
||||
}}
|
||||
filamentChoices={filamentChoices}
|
||||
sourceOverrides={sourceOverrides}
|
||||
sourceSelected={selected}
|
||||
onToggleSource={(key, on) =>
|
||||
setSelected((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (on) next.add(key);
|
||||
else next.delete(key);
|
||||
return next;
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/** Renders the panel and waits for its dynamically imported metadata. */
|
||||
async function renderPanel(initial: Record<string, SettingValue> = {}) {
|
||||
async function renderPanel(
|
||||
initial: Record<string, SettingValue> = {},
|
||||
extra: {
|
||||
sourceOverrides?: DesignOverride[];
|
||||
initialSelected?: string[];
|
||||
filamentChoices?: FilamentChoice[];
|
||||
} = {},
|
||||
) {
|
||||
const onChange = vi.fn();
|
||||
render(<Harness initial={initial} onChange={onChange} />);
|
||||
render(<Harness initial={initial} onChange={onChange} {...extra} />);
|
||||
await waitFor(() => expect(screen.getByPlaceholderText('Search settings')).toBeInTheDocument());
|
||||
return { onChange };
|
||||
}
|
||||
|
|
@ -177,3 +203,150 @@ describe('SlicerSettingsPanel', () => {
|
|||
expect(values.wall_loops).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SlicerSettingsPanel — search', () => {
|
||||
it('treats underscores and spaces alike so a key can be typed naturally', async () => {
|
||||
// outer_wall_speed's label is only "Outer wall" — the Speed page supplies
|
||||
// the rest — so the key is the only place the full phrase appears.
|
||||
const user = userEvent.setup();
|
||||
await renderPanel();
|
||||
await user.click(screen.getByRole('button', { name: 'Expert' }));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'outer wall speed');
|
||||
await waitFor(() => expect(screen.getByLabelText(/^Outer wall/)).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('matches a page or group name, not just option labels', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel();
|
||||
await user.click(screen.getByRole('button', { name: 'Expert' }));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'ironing');
|
||||
await waitFor(() => expect(screen.getByLabelText(/^Ironing type/)).toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
|
||||
describe("SlicerSettingsPanel — the source file's own settings", () => {
|
||||
const sourceOverrides: DesignOverride[] = [
|
||||
{ key: 'wall_loops', value: '5', printer_coupled: false },
|
||||
{ key: 'outer_wall_speed', value: '200', printer_coupled: true },
|
||||
// A key the vendored schema has no entry for. It still applies, so it must
|
||||
// not silently vanish from a panel that claims to show what will be used.
|
||||
{ key: 'some_unlisted_key', value: '7', printer_coupled: false },
|
||||
];
|
||||
|
||||
it("shows the designer's value against the option once switched on", async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { sourceOverrides, initialSelected: ['wall_loops'] });
|
||||
const input = await showOption(user, 'Wall loops', 'wall loops');
|
||||
expect(input).toHaveValue(5);
|
||||
expect(screen.getByText('from file')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('falls back to the preset value when it is switched off', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { sourceOverrides, initialSelected: [] });
|
||||
// wall_loops defaults to 2 in the schema.
|
||||
const input = await showOption(user, 'Wall loops', 'wall loops');
|
||||
expect(input).toHaveValue(2);
|
||||
});
|
||||
|
||||
it('flags a machine-coupled setting rather than applying it quietly', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { sourceOverrides, initialSelected: ['wall_loops'] });
|
||||
await user.click(screen.getByRole('button', { name: 'Expert' }));
|
||||
await user.type(screen.getByPlaceholderText('Search settings'), 'outer wall speed');
|
||||
await waitFor(() => expect(screen.getByText("designer's printer")).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it('lists source settings the schema has no entry for', async () => {
|
||||
await renderPanel({}, { sourceOverrides, initialSelected: ['some_unlisted_key'] });
|
||||
await waitFor(() => expect(screen.getByText('Other settings from this file')).toBeInTheDocument());
|
||||
expect(screen.getByText('some_unlisted_key')).toBeInTheDocument();
|
||||
expect(screen.getByText('7')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps a typed value ahead of the file\'s', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onChange } = await renderPanel({}, { sourceOverrides, initialSelected: ['wall_loops'] });
|
||||
|
||||
const input = await showOption(user, 'Wall loops', 'wall loops');
|
||||
expect(input).toHaveValue(5);
|
||||
await user.clear(input);
|
||||
await user.type(input, '3');
|
||||
|
||||
// The typed value is what gets sent; the file's tick is unaffected and the
|
||||
// backend applies it first, so last-write-wins leaves 3 in the process JSON.
|
||||
await waitFor(() => {
|
||||
const [, serialized] = onChange.mock.calls.at(-1)!;
|
||||
expect(serialized.wall_loops).toBe('3');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SlicerSettingsPanel — filament-slot options', () => {
|
||||
const filamentChoices: FilamentChoice[] = [
|
||||
{ index: 1, label: 'Bambu PLA Basic', color: '#FF0000' },
|
||||
{ index: 2, label: 'Bambu Support for PLA', color: '#FFFFFF' },
|
||||
];
|
||||
|
||||
it("follows the slicer's own gating rather than being live regardless", async () => {
|
||||
// The interface picker sits behind have_support_material, so it greys out
|
||||
// with supports off — becoming a dropdown must not exempt it from the
|
||||
// rules every other option obeys.
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { filamentChoices });
|
||||
const off = await showOption(user, 'Support/raft interface', 'support_interface_filament');
|
||||
expect(off).toBeDisabled();
|
||||
});
|
||||
|
||||
it('is operable once supports are switched on', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({ enable_support: true }, { filamentChoices });
|
||||
const on = await showOption(user, 'Support/raft interface', 'support_interface_filament');
|
||||
expect(on).toBeEnabled();
|
||||
});
|
||||
|
||||
it('offers the picked filaments instead of a bare number field', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { filamentChoices });
|
||||
const control = await showOption(user, 'Support/raft base', 'support_filament');
|
||||
|
||||
expect(control.tagName).toBe('SELECT');
|
||||
const labels = Array.from((control as HTMLSelectElement).options).map((o) => o.textContent);
|
||||
expect(labels).toEqual(['Default', '1: Bambu PLA Basic', '2: Bambu Support for PLA']);
|
||||
});
|
||||
|
||||
it("defaults to the slicer's 0, meaning no specific filament", async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { filamentChoices });
|
||||
const control = await showOption(user, 'Support/raft base', 'support_filament');
|
||||
expect(control).toHaveValue('0');
|
||||
});
|
||||
|
||||
it('sends the slot index the slicer expects', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onChange } = await renderPanel({ enable_support: true }, { filamentChoices });
|
||||
const control = await showOption(user, 'Support/raft interface', 'support_interface_filament');
|
||||
|
||||
await user.selectOptions(control, '2');
|
||||
await waitFor(() => {
|
||||
const [, serialized] = onChange.mock.calls.at(-1)!;
|
||||
expect(serialized.support_interface_filament).toBe('2');
|
||||
});
|
||||
});
|
||||
|
||||
it('stays a plain number field when no filaments have been picked', async () => {
|
||||
// STL sources and the pre-plate-analysis window have no slot list yet;
|
||||
// an empty dropdown would be worse than the number input it replaced.
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { filamentChoices: [] });
|
||||
const control = await showOption(user, 'Support/raft base', 'support_filament');
|
||||
expect(control.tagName).toBe('INPUT');
|
||||
});
|
||||
|
||||
it('leaves unrelated integer options alone', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderPanel({}, { filamentChoices });
|
||||
const control = await showOption(user, 'Wall loops', 'wall loops');
|
||||
expect(control.tagName).toBe('INPUT');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -485,3 +485,61 @@ describe('presetCompatibility — nozzle-only @<size> tag (#2628 follow-up)', ()
|
|||
).toBe('mismatch');
|
||||
});
|
||||
});
|
||||
|
||||
describe("presetCompatibility — BambuStudio's \"# \" user-clone prefix", () => {
|
||||
const index = buildCompatibilityIndex(PRINTER_MODELS);
|
||||
// Editing a system preset saves a copy under this name; .bbscfg bundle
|
||||
// exports use the same convention. The backend already normalises it in
|
||||
// _canonical_printer_model.
|
||||
const CLONED_X1C = '# Bambu Lab X1 Carbon 0.4 nozzle';
|
||||
|
||||
it('still matches a printer-tagged preset when the printer is a clone', () => {
|
||||
// Regression: the prefix failed the "Bambu Lab …" test, so every preset
|
||||
// came back 'unknown' and the dropdown filter silently did nothing.
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL X1C' }, 'process', CLONED_X1C, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('still rules out another printer when the selected printer is a clone', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL H2D' }, 'process', CLONED_X1C, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('matches a cloned preset against an unprefixed printer', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '# 0.20mm Standard @BBL X1C' }, 'process', X1C, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('still compares the nozzle size through the prefix', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '0.20mm Standard @BBL X1C 0.6 nozzle' }, 'process', CLONED_X1C, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('matches compatible_printers with the prefix on either side', () => {
|
||||
// A preset cloned from a system printer lists the *unprefixed* name; a raw
|
||||
// comparison against the "# " form reads as a mismatch, which now hides
|
||||
// the preset rather than merely demoting it.
|
||||
expect(
|
||||
presetCompatibility({ name: 'My Process', compatible_printers: [X1C] }, 'process', CLONED_X1C, index),
|
||||
).toBe('match');
|
||||
expect(
|
||||
presetCompatibility({ name: 'My Process', compatible_printers: [CLONED_X1C] }, 'process', X1C, index),
|
||||
).toBe('match');
|
||||
});
|
||||
|
||||
it('does not let the prefix turn a genuine mismatch into a match', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: 'My Process', compatible_printers: [P2S] }, 'process', CLONED_X1C, index),
|
||||
).toBe('mismatch');
|
||||
});
|
||||
|
||||
it('leaves an untagged clone unknown rather than guessing', () => {
|
||||
expect(
|
||||
presetCompatibility({ name: '# My own profile' }, 'process', CLONED_X1C, index),
|
||||
).toBe('unknown');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import { describe, it, expect } from 'vitest';
|
|||
|
||||
import processSchema from '../../data/slicer/process-schema.json';
|
||||
import processToggles from '../../data/slicer/process-toggle-rules.json';
|
||||
import processTree from '../../data/slicer/process-ui-tree.json';
|
||||
import { disabledKeys, makeConfigReader } from '../../lib/slicerToggle';
|
||||
import type { ProcessSchema, SettingValue } from '../../types/slicerSettings';
|
||||
import type { ProcessSchema, ProcessUiTree, SettingValue } from '../../types/slicerSettings';
|
||||
|
||||
const schema = processSchema as unknown as ProcessSchema;
|
||||
const tree = processTree as unknown as ProcessUiTree;
|
||||
const toggles = processToggles as { locals: Record<string, string>; rules: Array<{ fields: string[]; enable_if: string }> };
|
||||
|
||||
const disabled = (settings: Record<string, SettingValue>) => disabledKeys(settings, schema, toggles);
|
||||
|
|
@ -93,3 +95,43 @@ describe('disabledKeys', () => {
|
|||
expect(decided.length).toBeGreaterThanOrEqual(Math.floor(toggles.rules.length * 0.6));
|
||||
});
|
||||
});
|
||||
|
||||
describe('vendored process schema', () => {
|
||||
// The extractor reads defaults and bounds out of C++ initialisers, so float
|
||||
// literals arrive in source form — `0.`, `0.3f`, `100.%`, and `0.f` split
|
||||
// into [0, "f"]. Rendering those verbatim put a column of "0." in the Line
|
||||
// width group. scripts/generate-slicer-schema.mjs normalises them; this
|
||||
// guards a regeneration that drops that step.
|
||||
const LITERAL_ARTEFACT = /^-?[\d]*\.$|\.%$|^-?[\d.]+f$/;
|
||||
|
||||
const offenders = (field: 'default' | 'min' | 'max') =>
|
||||
Object.entries(schema)
|
||||
.filter(([, opt]) => {
|
||||
const v = opt[field];
|
||||
if (Array.isArray(v)) return v.some((x) => x === 'f');
|
||||
return typeof v === 'string' && LITERAL_ARTEFACT.test(v);
|
||||
})
|
||||
.map(([key]) => `${key}.${field}`);
|
||||
|
||||
it.each(['default', 'min', 'max'] as const)('carries no C++ literal artefacts in %s', (field) => {
|
||||
expect(offenders(field)).toEqual([]);
|
||||
});
|
||||
|
||||
it('renders the line-width defaults as plain numbers', () => {
|
||||
// The reported symptom: every field in this group showed "0."
|
||||
for (const key of ['line_width', 'outer_wall_line_width', 'inner_wall_line_width', 'support_line_width']) {
|
||||
expect(schema[key].default).toBe('0');
|
||||
}
|
||||
expect(schema.bridge_line_width.default).toBe('100%');
|
||||
});
|
||||
|
||||
it('keeps every option the UI tree references', () => {
|
||||
// A trim that drops a referenced key renders a control with no type,
|
||||
// label or default.
|
||||
for (const page of tree) {
|
||||
for (const group of page.groups) {
|
||||
for (const key of group.options) expect(schema[key]).toBeDefined();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Cloud, CloudOff, Cog, Loader2, RefreshCw, X } from 'lucide-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useId, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
|
|
@ -17,7 +17,7 @@ import { useSliceJobTracker } from '../contexts/SliceJobTrackerContext';
|
|||
import { useToast } from '../contexts/ToastContext';
|
||||
import { useIsWideLayout } from '../hooks/useIsWideLayout';
|
||||
import { PlatePickerModal } from './PlatePickerModal';
|
||||
import SlicerSettingsPanel from './SlicerSettingsPanel';
|
||||
import SlicerSettingsPanel, { type FilamentChoice } from './SlicerSettingsPanel';
|
||||
import type { DesignOverride, PlateFilament } from '../types/plates';
|
||||
import type { SettingValue } from '../types/slicerSettings';
|
||||
import {
|
||||
|
|
@ -189,16 +189,6 @@ function formatElapsed(seconds: number): string {
|
|||
return `${h}h ${remM}m`;
|
||||
}
|
||||
|
||||
// Render a slicer parameter value for the design-settings list. Bambu's process
|
||||
// schema stores everything as strings or arrays of strings, so this only has to
|
||||
// flatten arrays and keep scalars readable — no unit or type interpretation,
|
||||
// which would rot against every slicer release.
|
||||
function formatDesignValue(value: unknown): string {
|
||||
if (Array.isArray(value)) return value.map((v) => String(v)).join(', ');
|
||||
if (value == null) return '';
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { trackJob } = useSliceJobTracker();
|
||||
|
|
@ -256,7 +246,6 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
// accelerations, prime-tower geometry) are listed but start unticked — those
|
||||
// were tuned for the designer's printer and can be plain wrong on another.
|
||||
const [designKeys, setDesignKeys] = useState<Set<string>>(new Set());
|
||||
const [designExpanded, setDesignExpanded] = useState(false);
|
||||
|
||||
// Process settings the user edited by hand in the settings panel. Two shapes
|
||||
// are kept: the panel's editing values, and the same set serialised into the
|
||||
|
|
@ -432,6 +421,24 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
[printerModelsQuery.data],
|
||||
);
|
||||
|
||||
// Slot list for the settings panel's filament pickers (support base and
|
||||
// interface, and the Multimaterial page's per-region options). Those store a
|
||||
// plain integer, so without this the user has to map slot numbers onto their
|
||||
// own AMS by hand. Falls back to the slot's material when a slot has no pick
|
||||
// yet, so the list is never a column of blanks.
|
||||
const filamentChoices = useMemo<FilamentChoice[]>(() => {
|
||||
const data = presetsQuery.data;
|
||||
return filamentSlots.map((slot, idx) => {
|
||||
const ref = filamentPresets[idx] ?? null;
|
||||
const preset = data && ref ? findPreset(data, ref, 'filament') : null;
|
||||
return {
|
||||
index: idx + 1,
|
||||
label: preset?.name || slot.type || t('slice.filamentSlotUnset', 'not set'),
|
||||
color: slot.color || undefined,
|
||||
};
|
||||
});
|
||||
}, [filamentSlots, filamentPresets, presetsQuery.data, t]);
|
||||
|
||||
// Printer / process preset names the source 3MF was prepared with. The
|
||||
// plates query resolves before the presets query (the latter is gated on
|
||||
// it), so these are known by the time the pre-pick effects run.
|
||||
|
|
@ -857,68 +864,6 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
selectedPrinterName={selectedPrinterName}
|
||||
compatIndex={compatIndex}
|
||||
/>
|
||||
{/* Designer's process tweaks (#2622). BambuStudio records which
|
||||
keys deviate from the stock preset in the 3MF itself, so a
|
||||
re-slice for another printer can carry them instead of
|
||||
flattening them under --load-settings. Hidden entirely when
|
||||
the source lists none, and disabled in embedded mode where
|
||||
the process JSON these patch is never sent. */}
|
||||
{designOverrides.length > 0 && (
|
||||
<div className="rounded-lg border border-bambu-dark-tertiary bg-bambu-dark/40 p-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDesignExpanded((v) => !v)}
|
||||
className="flex w-full items-center justify-between gap-2 text-left"
|
||||
>
|
||||
<span className="text-sm text-white">
|
||||
{t('slice.designSettings')}
|
||||
<span className="block text-xs text-bambu-gray/70">
|
||||
{t('slice.designSettingsHint', { count: designOverrides.length })}
|
||||
</span>
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-bambu-gray">
|
||||
{t('slice.designSettingsSelected', { selected: designKeys.size, total: designOverrides.length })}
|
||||
</span>
|
||||
</button>
|
||||
{designExpanded && (
|
||||
<div className="mt-3 space-y-1.5 border-t border-bambu-dark-tertiary pt-3">
|
||||
{designOverrides.map((o) => (
|
||||
<label
|
||||
key={o.key}
|
||||
className={`flex items-start gap-2 text-xs ${useEmbedded ? 'opacity-50' : 'cursor-pointer'}`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={designKeys.has(o.key)}
|
||||
disabled={isEnqueuing || useEmbedded}
|
||||
onChange={(e) => {
|
||||
setDesignKeys((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (e.target.checked) next.add(o.key);
|
||||
else next.delete(o.key);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
className="mt-0.5 shrink-0 cursor-pointer"
|
||||
/>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="font-mono text-bambu-gray">{o.key}</span>
|
||||
<span className="ml-1.5 break-all text-white">{formatDesignValue(o.value)}</span>
|
||||
{o.printer_coupled && (
|
||||
<span
|
||||
className="ml-1.5 rounded bg-amber-100 px-1 py-0.5 text-[10px] text-amber-700 dark:bg-amber-500/20 dark:text-amber-400"
|
||||
title={t('slice.designSettingsPrinterCoupledHint')}
|
||||
>
|
||||
{t('slice.designSettingsPrinterCoupled')}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bed-type override (#1337). Always visible, always enabled.
|
||||
The backend patches curr_bed_type on the resolved process
|
||||
|
|
@ -1026,13 +971,17 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
{/* Right column: the settings panel. It owns this column, so
|
||||
there is nothing to collapse it out of the way of — the
|
||||
disclosure below lg exists only because the single-column
|
||||
stack cannot afford 348 options unfolded. */}
|
||||
stack cannot afford 348 options unfolded.
|
||||
|
||||
Kept on screen in embedded mode but disabled rather than
|
||||
removed: nothing here is sent on that path (the file's own
|
||||
settings drive the slice), and dropping the column outright
|
||||
made the dialog look like it had lost a feature whenever
|
||||
the toggle was flipped. */}
|
||||
<div className="mt-4 lg:mt-0 min-w-0">
|
||||
{/* Process settings, mirroring OrcaSlicer's own Print Settings
|
||||
tabs. Hidden entirely in embedded mode, where no process
|
||||
JSON is sent for these to patch. */}
|
||||
{!useEmbedded && (
|
||||
<div className="rounded border border-bambu-dark-tertiary p-3">
|
||||
<div
|
||||
className={`rounded border border-bambu-dark-tertiary p-3 ${useEmbedded ? 'opacity-60' : ''}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSettingsExpanded((v) => !v)}
|
||||
|
|
@ -1043,15 +992,25 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
<span className="text-sm text-white">
|
||||
{t('slice.processSettings', 'Process settings')}
|
||||
<span className="block text-xs text-bambu-gray/70">
|
||||
{t('slice.processSettingsHint', "Adjust the picked preset for this slice. Anything you don't touch stays as the preset defines it.")}
|
||||
{useEmbedded
|
||||
? t(
|
||||
'slice.processSettingsEmbedded',
|
||||
"Not used while \"Use the file's built-in settings\" is on -- the file's own settings drive this slice.",
|
||||
)
|
||||
: t(
|
||||
'slice.processSettingsHint',
|
||||
"Adjust the picked preset for this slice. Anything you don't touch stays as the preset defines it.",
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-bambu-gray">
|
||||
{Object.keys(serializedProcessOverrides).length > 0
|
||||
? t('slice.processSettingsChanged', '{{count}} changed', {
|
||||
count: Object.keys(serializedProcessOverrides).length,
|
||||
})
|
||||
: t('slice.processSettingsUnchanged', 'Preset defaults')}
|
||||
{useEmbedded
|
||||
? t('slice.processSettingsInactive', 'Inactive')
|
||||
: Object.keys(serializedProcessOverrides).length > 0
|
||||
? t('slice.processSettingsChanged', '{{count}} changed', {
|
||||
count: Object.keys(serializedProcessOverrides).length,
|
||||
})
|
||||
: t('slice.processSettingsUnchanged', 'Preset defaults')}
|
||||
</span>
|
||||
</button>
|
||||
{panelOpen && (
|
||||
|
|
@ -1062,12 +1021,28 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
|||
setProcessOverrides(values);
|
||||
setSerializedProcessOverrides(serialized);
|
||||
}}
|
||||
disabled={isEnqueuing}
|
||||
disabled={isEnqueuing || useEmbedded}
|
||||
// The designer's own deviations (#2622) are shown
|
||||
// against the options they belong to rather than in
|
||||
// a list of their own. Only the tick state lives
|
||||
// here; the values still travel as design_overrides,
|
||||
// so the backend keeps reading them from the file
|
||||
// and keys outside the vendored schema stay faithful.
|
||||
filamentChoices={filamentChoices}
|
||||
sourceOverrides={designOverrides}
|
||||
sourceSelected={designKeys}
|
||||
onToggleSource={(key, on) =>
|
||||
setDesignKeys((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (on) next.add(key);
|
||||
else next.delete(key);
|
||||
return next;
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -1259,8 +1234,8 @@ interface PresetDropdownProps {
|
|||
// configuring against the source 3MF's per-slot colour.
|
||||
swatchColor?: string;
|
||||
// Selected printer context (#1325). When provided for a process / filament
|
||||
// slot, presets that resolve to a different printer (per compatIndex) move
|
||||
// into a trailing "Other printers" group instead of the main tier list.
|
||||
// slot, presets that resolve to a different printer (per compatIndex) are
|
||||
// held back behind a "Show all" link instead of padding out the main list.
|
||||
selectedPrinterName?: string | null;
|
||||
compatIndex?: PrinterCompatibilityIndex;
|
||||
}
|
||||
|
|
@ -1277,6 +1252,14 @@ function PresetDropdown({
|
|||
compatIndex,
|
||||
}: PresetDropdownProps) {
|
||||
const { t } = useTranslation();
|
||||
// Reveals the other-printer group for this slot only. Per-dropdown rather
|
||||
// than modal-wide: wanting a filament from another printer's library says
|
||||
// nothing about wanting its process profiles too.
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
// Binds the label to the select now that they are siblings rather than
|
||||
// nested. Filament slots render several of these, so the id must be unique
|
||||
// per instance rather than derived from the slot name.
|
||||
const selectId = useId();
|
||||
|
||||
// Tier sections (imported → cloud → standard), plus — for a process /
|
||||
// filament slot with a selected printer — a trailing group of presets that
|
||||
|
|
@ -1322,12 +1305,30 @@ function PresetDropdown({
|
|||
return { sections: compatSections, otherEntries: other };
|
||||
}, [data, slot, t, selectedPrinterName, compatIndex]);
|
||||
|
||||
// Other-printer presets are held back by default so the list shows what is
|
||||
// usable on the selected printer. Two things are never hidden: a preset whose
|
||||
// compatibility is merely *unknown* (it never reaches otherEntries), and the
|
||||
// one currently selected — a pipeline or an auto-pick can land on a
|
||||
// cross-printer preset, and dropping it from the options would blank the
|
||||
// select and silently discard the choice.
|
||||
const selectedRefValue = toRefValue(value);
|
||||
const visibleOther = useMemo(() => {
|
||||
if (showAll) return otherEntries;
|
||||
return otherEntries.filter((p) => `${p.source}:${p.id}` === selectedRefValue);
|
||||
}, [showAll, otherEntries, selectedRefValue]);
|
||||
|
||||
const hiddenCount = otherEntries.length - visibleOther.length;
|
||||
const totalEntries =
|
||||
sections.reduce((sum, s) => sum + s.entries.length, 0) + otherEntries.length;
|
||||
sections.reduce((sum, s) => sum + s.entries.length, 0) + visibleOther.length;
|
||||
|
||||
return (
|
||||
<label className="block">
|
||||
<span className="flex items-center gap-2 text-xs text-bambu-gray mb-1">
|
||||
// A plain wrapper rather than a <label> around everything: the "Show all"
|
||||
// control is a button, and a button inside a label that also wraps the
|
||||
// select inherits the whole label as its accessible name (screen readers
|
||||
// announced it as "Process profile 2 hidden 0.20mm Standard @BBL X1C") as
|
||||
// well as being invalid HTML. The label is bound to the select by id.
|
||||
<div className="block">
|
||||
<div className="flex items-center gap-2 text-xs text-bambu-gray mb-1">
|
||||
{swatchColor && (
|
||||
<span
|
||||
className="inline-block w-3 h-3 rounded-full border border-bambu-dark-tertiary"
|
||||
|
|
@ -1335,9 +1336,29 @@ function PresetDropdown({
|
|||
aria-hidden
|
||||
/>
|
||||
)}
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<label htmlFor={selectId}>{label}</label>
|
||||
{(hiddenCount > 0 || showAll) && (
|
||||
<span className="ml-auto flex items-center gap-1.5 font-normal">
|
||||
{hiddenCount > 0 && (
|
||||
<span className="text-bambu-gray/60">
|
||||
{t('slice.presetsHidden', '{{count}} hidden', { count: hiddenCount })}
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAll((v) => !v)}
|
||||
disabled={disabled}
|
||||
className="text-bambu-green hover:underline disabled:opacity-50 disabled:no-underline"
|
||||
>
|
||||
{showAll
|
||||
? t('slice.showFewerPresets', 'Show fewer')
|
||||
: t('slice.showAllPresets', 'Show all')}
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<select
|
||||
id={selectId}
|
||||
value={toRefValue(value)}
|
||||
onChange={(e) => onChange(fromRefValue(e.target.value))}
|
||||
disabled={disabled || totalEntries === 0}
|
||||
|
|
@ -1357,9 +1378,9 @@ function PresetDropdown({
|
|||
))}
|
||||
</optgroup>
|
||||
))}
|
||||
{otherEntries.length > 0 && (
|
||||
{visibleOther.length > 0 && (
|
||||
<optgroup label={t('slice.otherPrinters')}>
|
||||
{otherEntries.map((p) => (
|
||||
{visibleOther.map((p) => (
|
||||
<option key={`${p.source}:${p.id}`} value={`${p.source}:${p.id}`}>
|
||||
{p.name}
|
||||
</option>
|
||||
|
|
@ -1367,6 +1388,6 @@ function PresetDropdown({
|
|||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Search, RotateCcw, Loader2 } from 'lucide-react';
|
||||
import { Search, RotateCcw, Loader2, ChevronDown } from 'lucide-react';
|
||||
|
||||
import { disabledKeys, type ToggleRules } from '../lib/slicerToggle';
|
||||
import { defaultForDisplay, displaySidetext, isModified, numericBound, serializeOverrides } from '../lib/slicerSettings';
|
||||
import type { OptionMode, ProcessOption, ProcessSchema, ProcessUiTree, SettingValue } from '../types/slicerSettings';
|
||||
import type { DesignOverride } from '../types/plates';
|
||||
|
||||
interface SlicerData {
|
||||
schema: ProcessSchema;
|
||||
|
|
@ -45,13 +46,73 @@ interface Props {
|
|||
*/
|
||||
onChange: (values: Record<string, SettingValue>, serialized: Record<string, string | string[]>) => void;
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Process settings the source 3MF's designer moved off the stock preset
|
||||
* (#2622), as recorded by BambuStudio in `different_settings_to_system`.
|
||||
*
|
||||
* These are shown inline against the options they belong to rather than in a
|
||||
* list of their own, so there is one place to see what this slice will use.
|
||||
* Their *values* are not routed through this component: the backend reads
|
||||
* them straight out of the file, which keeps settings faithful even for keys
|
||||
* outside the option schema we vendor. All this panel decides is which of
|
||||
* them are switched on.
|
||||
*/
|
||||
sourceOverrides?: DesignOverride[];
|
||||
/** Which source-override keys are currently switched on. */
|
||||
sourceSelected?: Set<string>;
|
||||
onToggleSource?: (key: string, on: boolean) => void;
|
||||
/**
|
||||
* The filaments picked on the slice dialog's left-hand side, in slot order.
|
||||
*
|
||||
* A handful of options select *which filament* prints a given feature —
|
||||
* supports, outer walls, infill. The slicer stores those as a plain integer
|
||||
* where 0 means "whatever filament the region already uses" and 1..N is a
|
||||
* slot. A bare number field makes the user count their own AMS slots, so
|
||||
* when this is supplied those options become a dropdown of the actual
|
||||
* picks instead.
|
||||
*/
|
||||
filamentChoices?: FilamentChoice[];
|
||||
}
|
||||
|
||||
export interface FilamentChoice {
|
||||
/** 1-based slot index, matching the integer the slicer stores. */
|
||||
index: number;
|
||||
/** Preset name, or a fallback when the slot has no pick yet. */
|
||||
label: string;
|
||||
/** Slot colour from the source plate, for the swatch. */
|
||||
color?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options whose integer value names a filament slot rather than a quantity.
|
||||
* All use the same encoding: 0 = "default / current filament", 1..N = slot.
|
||||
* Support base and interface are the pair on the Support page; the rest are
|
||||
* the Multimaterial page's per-region pickers, which have the same wart.
|
||||
*/
|
||||
const FILAMENT_SLOT_OPTIONS = new Set([
|
||||
'support_filament',
|
||||
'support_interface_filament',
|
||||
'outer_wall_filament_id',
|
||||
'inner_wall_filament_id',
|
||||
'top_surface_filament_id',
|
||||
'bottom_surface_filament_id',
|
||||
'internal_solid_filament_id',
|
||||
'sparse_infill_filament_id',
|
||||
]);
|
||||
|
||||
/** Visibility tiers, in increasing order of how much they reveal. */
|
||||
const MODES: OptionMode[] = ['simple', 'advanced', 'expert'];
|
||||
const MODE_RANK: Record<string, number> = { simple: 0, advanced: 1, expert: 2, develop: 3 };
|
||||
|
||||
export default function SlicerSettingsPanel({ values, onChange, disabled = false }: Props) {
|
||||
export default function SlicerSettingsPanel({
|
||||
values,
|
||||
onChange,
|
||||
disabled = false,
|
||||
sourceOverrides = [],
|
||||
sourceSelected,
|
||||
onToggleSource,
|
||||
filamentChoices,
|
||||
}: Props) {
|
||||
const { t } = useTranslation();
|
||||
const [data, setData] = useState<SlicerData | null>(null);
|
||||
const [mode, setMode] = useState<OptionMode>('simple');
|
||||
|
|
@ -84,6 +145,19 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
[data, values],
|
||||
);
|
||||
|
||||
const sourceByKey = useMemo(
|
||||
() => new Map(sourceOverrides.map((o) => [o.key, o])),
|
||||
[sourceOverrides],
|
||||
);
|
||||
|
||||
// Source overrides for keys the vendored schema doesn't cover. They still
|
||||
// apply — the backend reads their values from the file — so they get a group
|
||||
// of their own rather than being dropped from view.
|
||||
const unlistedSource = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return sourceOverrides.filter((o) => !data.schema[o.key]);
|
||||
}, [data, sourceOverrides]);
|
||||
|
||||
const emit = (next: Record<string, SettingValue>) => {
|
||||
if (!data) return;
|
||||
// Only genuine deviations are worth sending: an override that equals the
|
||||
|
|
@ -106,19 +180,30 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
// Search cuts across every page; without a query we show the selected page.
|
||||
const visiblePages = useMemo(() => {
|
||||
if (!data) return [];
|
||||
const needle = query.trim().toLowerCase();
|
||||
// Underscores and spaces are interchangeable so "outer wall speed" finds
|
||||
// `outer_wall_speed`. That matters more than it looks: several labels are
|
||||
// only meaningful with their group ("Outer wall" under Speed), so the key
|
||||
// is often the only place the full phrase appears.
|
||||
const flatten = (s: string) => s.toLowerCase().replace(/[_\s]+/g, ' ').trim();
|
||||
const needle = flatten(query);
|
||||
const withinMode = (key: string) => MODE_RANK[data.schema[key]?.mode ?? 'expert'] <= MODE_RANK[mode];
|
||||
const matches = (key: string) => {
|
||||
const matches = (key: string, group: string, page: string) => {
|
||||
if (!needle) return true;
|
||||
const opt = data.schema[key];
|
||||
return key.includes(needle) || opt?.label?.toLowerCase().includes(needle) || opt?.tooltip?.toLowerCase().includes(needle);
|
||||
// Group and page are matched too, so "speed" lists the Speed page's
|
||||
// options rather than only the handful with "speed" in their label.
|
||||
const haystack = [key, opt?.label ?? '', opt?.tooltip ?? '', group, page];
|
||||
return haystack.some((h) => flatten(h).includes(needle));
|
||||
};
|
||||
|
||||
return data.tree
|
||||
.map((p) => ({
|
||||
...p,
|
||||
groups: p.groups
|
||||
.map((g) => ({ ...g, options: g.options.filter((k) => withinMode(k) && matches(k)) }))
|
||||
.map((g) => ({
|
||||
...g,
|
||||
options: g.options.filter((k) => withinMode(k) && matches(k, g.group, p.page)),
|
||||
}))
|
||||
.filter((g) => g.options.length > 0),
|
||||
}))
|
||||
.filter((p) => p.groups.length > 0);
|
||||
|
|
@ -149,7 +234,7 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="flex rounded overflow-hidden border border-white/10">
|
||||
<div className="flex overflow-hidden rounded border border-bambu-dark-tertiary">
|
||||
{MODES.map((m) => (
|
||||
<button
|
||||
key={m}
|
||||
|
|
@ -157,7 +242,7 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
onClick={() => setMode(m)}
|
||||
disabled={disabled}
|
||||
className={`px-2.5 py-1 text-xs capitalize transition-colors ${
|
||||
mode === m ? 'bg-bambu-green text-black' : 'text-bambu-gray hover:text-white'
|
||||
mode === m ? 'bg-bambu-green text-white' : 'text-bambu-gray hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{t(`slicerSettings.mode.${m}`, m)}
|
||||
|
|
@ -173,7 +258,7 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
onChange={(e) => setQuery(e.target.value)}
|
||||
disabled={disabled}
|
||||
placeholder={t('slicerSettings.searchPlaceholder', 'Search settings')}
|
||||
className="w-full bg-black/30 border border-white/10 rounded pl-7 pr-2 py-1 text-xs text-white placeholder:text-bambu-gray/60"
|
||||
className="w-full rounded border border-bambu-dark-tertiary bg-bambu-dark pl-7 pr-2 py-1 text-xs text-white placeholder:text-bambu-gray/60 focus:border-bambu-green focus:outline-none disabled:opacity-40"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -199,7 +284,7 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
onClick={() => setPage(p.page)}
|
||||
disabled={disabled}
|
||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
||||
activePage?.page === p.page ? 'bg-white/10 text-white' : 'text-bambu-gray hover:text-white'
|
||||
activePage?.page === p.page ? 'bg-bambu-dark-tertiary text-white' : 'text-bambu-gray hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{p.page}
|
||||
|
|
@ -221,7 +306,7 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
{query.trim() && <p className="text-[0.7rem] uppercase tracking-wide text-bambu-gray/70">{p.page}</p>}
|
||||
{p.groups.map((g) => (
|
||||
<fieldset key={`${p.page}:${g.group}`} className="flex flex-col gap-1.5">
|
||||
<legend className="text-xs font-medium text-white/80 mb-1">{g.group}</legend>
|
||||
<legend className="mb-1 text-xs font-medium text-white">{g.group}</legend>
|
||||
{g.options.map((key) => (
|
||||
<OptionRow
|
||||
key={key}
|
||||
|
|
@ -231,12 +316,48 @@ export default function SlicerSettingsPanel({ values, onChange, disabled = false
|
|||
onChange={(v) => setValue(key, v)}
|
||||
disabled={disabled || off.has(key)}
|
||||
disabledBySlicer={off.has(key)}
|
||||
source={sourceByKey.get(key)}
|
||||
sourceOn={sourceSelected?.has(key) ?? false}
|
||||
onToggleSource={onToggleSource}
|
||||
filamentChoices={FILAMENT_SLOT_OPTIONS.has(key) ? filamentChoices : undefined}
|
||||
/>
|
||||
))}
|
||||
</fieldset>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Source-file settings the vendored schema has no entry for: they
|
||||
still apply (the backend reads their values from the file), so
|
||||
they get a plain key/value group rather than disappearing from a
|
||||
panel that claims to show what this slice will use. */}
|
||||
{unlistedSource.length > 0 && !query.trim() && (
|
||||
<fieldset className="flex flex-col gap-1.5">
|
||||
<legend className="mb-1 text-xs font-medium text-white">
|
||||
{t('slicerSettings.otherFromFile', 'Other settings from this file')}
|
||||
</legend>
|
||||
{unlistedSource.map((o) => (
|
||||
<label key={o.key} className="flex items-center gap-2 text-xs cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sourceSelected?.has(o.key) ?? false}
|
||||
disabled={disabled || !onToggleSource}
|
||||
onChange={(e) => onToggleSource?.(o.key, e.target.checked)}
|
||||
className="shrink-0 cursor-pointer disabled:opacity-40"
|
||||
/>
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
<span className="font-mono text-bambu-gray">{o.key}</span>
|
||||
<span className="ml-1.5 text-white">{formatSourceValue(o.value)}</span>
|
||||
</span>
|
||||
{o.printer_coupled && (
|
||||
<span className="shrink-0 rounded bg-amber-100 px-1 py-0.5 text-[10px] text-amber-700 dark:bg-amber-500/20 dark:text-amber-400">
|
||||
{t('slicerSettings.fromFilePrinterCoupled', "designer's printer")}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
))}
|
||||
</fieldset>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -251,13 +372,37 @@ interface RowProps {
|
|||
disabled: boolean;
|
||||
/** Greyed because the slicer's own rules turn it off, not because the form is busy. */
|
||||
disabledBySlicer: boolean;
|
||||
/** Set when the source file's designer moved this option off the stock preset. */
|
||||
source?: DesignOverride;
|
||||
sourceOn?: boolean;
|
||||
onToggleSource?: (key: string, on: boolean) => void;
|
||||
/** Set only for options whose integer value names a filament slot. */
|
||||
filamentChoices?: FilamentChoice[];
|
||||
}
|
||||
|
||||
function OptionRow({ optionKey, option, value, onChange, disabled, disabledBySlicer }: RowProps) {
|
||||
function OptionRow({
|
||||
optionKey,
|
||||
option,
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
disabledBySlicer,
|
||||
source,
|
||||
sourceOn = false,
|
||||
onToggleSource,
|
||||
filamentChoices,
|
||||
}: RowProps) {
|
||||
const { t } = useTranslation();
|
||||
const modified = isModified(option, value);
|
||||
const unit = displaySidetext(option);
|
||||
const current = value === undefined ? defaultForDisplay(option) : String(value);
|
||||
// What this slice will actually use, in precedence order: a value typed here
|
||||
// wins, then the designer's value if it is switched on, then the preset's.
|
||||
const current =
|
||||
value !== undefined
|
||||
? String(value)
|
||||
: sourceOn && source
|
||||
? formatSourceValue(source.value)
|
||||
: defaultForDisplay(option);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 group" title={option.tooltip}>
|
||||
|
|
@ -267,6 +412,24 @@ function OptionRow({ optionKey, option, value, onChange, disabled, disabledBySli
|
|||
>
|
||||
{option.label || optionKey}
|
||||
{modified && <span className="ml-1 text-bambu-green" aria-hidden="true">•</span>}
|
||||
{source && (
|
||||
<span
|
||||
className={`ml-1.5 rounded px-1 py-0.5 text-[10px] ${
|
||||
source.printer_coupled
|
||||
? 'bg-amber-100 text-amber-700 dark:bg-amber-500/20 dark:text-amber-400'
|
||||
: 'bg-bambu-green/15 text-bambu-green'
|
||||
}`}
|
||||
title={
|
||||
source.printer_coupled
|
||||
? t('slicerSettings.fromFilePrinterCoupledHint', "Tuned for the printer this file was designed for -- may be wrong or out of range on yours.")
|
||||
: t('slicerSettings.fromFileHint', "The designer changed this in the source file. Its value is {{value}}.", { value: formatSourceValue(source.value) })
|
||||
}
|
||||
>
|
||||
{source.printer_coupled
|
||||
? t('slicerSettings.fromFilePrinterCoupled', "designer's printer")
|
||||
: t('slicerSettings.fromFile', 'from file')}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
|
|
@ -276,8 +439,24 @@ function OptionRow({ optionKey, option, value, onChange, disabled, disabledBySli
|
|||
current={current}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
filamentChoices={filamentChoices}
|
||||
/>
|
||||
{unit && <span className="text-[0.65rem] text-bambu-gray/60 w-10 truncate">{unit}</span>}
|
||||
{source && onToggleSource && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sourceOn}
|
||||
disabled={disabled}
|
||||
onChange={(e) => onToggleSource(optionKey, e.target.checked)}
|
||||
aria-label={t('slicerSettings.useFromFile', "Use the source file's value for {{option}}", {
|
||||
option: option.label || optionKey,
|
||||
})}
|
||||
title={t('slicerSettings.useFromFile', "Use the source file's value for {{option}}", {
|
||||
option: option.label || optionKey,
|
||||
})}
|
||||
className="w-3 h-3 cursor-pointer disabled:opacity-40"
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange(undefined)}
|
||||
|
|
@ -292,16 +471,63 @@ function OptionRow({ optionKey, option, value, onChange, disabled, disabledBySli
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a value read out of the source file. Bambu's process config stores
|
||||
* everything as strings or arrays of strings, so this only has to flatten
|
||||
* arrays — no unit or type interpretation, which would rot against every
|
||||
* slicer release.
|
||||
*/
|
||||
function formatSourceValue(value: unknown): string {
|
||||
if (Array.isArray(value)) return value.map((v) => String(v)).join(', ');
|
||||
if (value == null) return '';
|
||||
return String(value);
|
||||
}
|
||||
|
||||
interface ControlProps {
|
||||
id: string;
|
||||
option: ProcessOption;
|
||||
current: string;
|
||||
onChange: (value: SettingValue | undefined) => void;
|
||||
disabled: boolean;
|
||||
filamentChoices?: FilamentChoice[];
|
||||
}
|
||||
|
||||
function OptionControl({ id, option, current, onChange, disabled }: ControlProps) {
|
||||
const inputClass = 'bg-black/30 border border-white/10 rounded px-1.5 py-0.5 text-xs text-white disabled:opacity-40 w-24';
|
||||
function OptionControl({ id, option, current, onChange, disabled, filamentChoices }: ControlProps) {
|
||||
const { t } = useTranslation();
|
||||
// Theme tokens rather than raw black/white: bambu-dark and
|
||||
// bambu-dark-tertiary are CSS variables that follow the active theme, and
|
||||
// `text-white` is remapped to --text-primary in index.css.
|
||||
const inputClass =
|
||||
'w-24 rounded border border-bambu-dark-tertiary bg-bambu-dark px-1.5 py-0.5 text-xs text-white focus:border-bambu-green focus:outline-none disabled:opacity-40';
|
||||
|
||||
// Filament-slot pickers come before the generic branches: the value is an
|
||||
// integer, but offering a spinner over "1, 2, 3" makes the user map slot
|
||||
// numbers to their own AMS by hand.
|
||||
if (filamentChoices && filamentChoices.length > 0) {
|
||||
const selected = filamentChoices.find((c) => String(c.index) === current);
|
||||
return (
|
||||
<div className="relative w-24">
|
||||
<select
|
||||
id={id}
|
||||
value={current}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className={`${inputClass} w-full cursor-pointer appearance-none pr-5`}
|
||||
// The full name rarely fits in the control, so the hover carries it.
|
||||
title={selected?.label}
|
||||
>
|
||||
{/* 0 is the slicer's "no specific filament — use the region's own". */}
|
||||
<option value="0">{t('slicerSettings.filamentDefault', 'Default')}</option>
|
||||
{filamentChoices.map((choice) => (
|
||||
<option key={choice.index} value={String(choice.index)}>
|
||||
{choice.index}: {choice.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDown className="pointer-events-none absolute right-1.5 top-1/2 h-3 w-3 -translate-y-1/2 text-bambu-gray" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (option.type === 'coBool') {
|
||||
return (
|
||||
|
|
@ -317,20 +543,26 @@ function OptionControl({ id, option, current, onChange, disabled }: ControlProps
|
|||
}
|
||||
|
||||
if (option.type === 'coEnum' && option.enum_values) {
|
||||
// Native select chrome is replaced the same way as everywhere else in
|
||||
// Bambuddy: appearance-none plus our own chevron, so the control matches
|
||||
// the app in both themes instead of whatever the browser paints.
|
||||
return (
|
||||
<select
|
||||
id={id}
|
||||
value={current}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className={inputClass}
|
||||
>
|
||||
{option.enum_values.map((v, i) => (
|
||||
<option key={v} value={v}>
|
||||
{option.enum_labels?.[i] ?? v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="relative w-24">
|
||||
<select
|
||||
id={id}
|
||||
value={current}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className={`${inputClass} w-full cursor-pointer appearance-none pr-5`}
|
||||
>
|
||||
{option.enum_values.map((v, i) => (
|
||||
<option key={v} value={v}>
|
||||
{option.enum_labels?.[i] ?? v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDown className="pointer-events-none absolute right-1.5 top-1/2 h-3 w-3 -translate-y-1/2 text-bambu-gray" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
1
frontend/src/data/slicer/process-schema.json
Normal file
1
frontend/src/data/slicer/process-schema.json
Normal file
File diff suppressed because one or more lines are too long
1
frontend/src/data/slicer/process-toggle-rules.json
Normal file
1
frontend/src/data/slicer/process-toggle-rules.json
Normal file
File diff suppressed because one or more lines are too long
1
frontend/src/data/slicer/process-ui-tree.json
Normal file
1
frontend/src/data/slicer/process-ui-tree.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -4279,6 +4279,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Standard',
|
||||
fromFile: 'aus Datei',
|
||||
fromFileHint: 'Der Designer hat dies in der Quelldatei geändert. Wert: {{value}}.',
|
||||
fromFilePrinterCoupled: 'Drucker des Designers',
|
||||
fromFilePrinterCoupledHint: 'Auf den Drucker abgestimmt, für den diese Datei erstellt wurde – auf Ihrem kann der Wert falsch oder außerhalb des Bereichs sein.',
|
||||
useFromFile: 'Wert aus der Quelldatei für {{option}} verwenden',
|
||||
otherFromFile: 'Weitere Einstellungen aus dieser Datei',
|
||||
loading: 'Slicer-Einstellungen werden geladen…',
|
||||
mode: {
|
||||
simple: 'Einfach',
|
||||
|
|
@ -4291,6 +4298,12 @@ export default {
|
|||
noMatches: 'Keine Einstellungen passen zu dieser Suche.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'nicht gewählt',
|
||||
processSettingsEmbedded: 'Wird nicht verwendet, solange „Integrierte Einstellungen der Datei verwenden“ aktiv ist – die Einstellungen der Datei bestimmen diesen Slice.',
|
||||
processSettingsInactive: 'Inaktiv',
|
||||
presetsHidden: '{{count}} ausgeblendet',
|
||||
showAllPresets: 'Alle anzeigen',
|
||||
showFewerPresets: 'Weniger anzeigen',
|
||||
processSettings: 'Prozesseinstellungen',
|
||||
processSettingsHint: 'Passen Sie das gewählte Profil für diesen Slice an. Alles, was Sie nicht ändern, bleibt wie im Profil definiert.',
|
||||
processSettingsChanged: '{{count}} geändert',
|
||||
|
|
@ -4324,11 +4337,6 @@ export default {
|
|||
autoOrientHint: 'Der Slicer dreht jedes Objekt zuerst auf die am besten druckbare Seite. Überschreibt die Ausrichtung aus der Datei.',
|
||||
autoArrange: 'Automatisch auf dem Druckbett anordnen',
|
||||
autoArrangeHint: 'Der Slicer verteilt die Objekte so, dass sie sich nicht mehr überlappen. Ersetzt die Anordnung aus der Datei.',
|
||||
designSettings: 'Einstellungen des Erstellers behalten',
|
||||
designSettingsHint: 'Diese Datei ändert {{count}} Druckeinstellung(en) gegenüber dem Standardprofil.',
|
||||
designSettingsSelected: '{{selected}} von {{total}} ausgewählt',
|
||||
designSettingsPrinterCoupled: 'druckerspezifisch',
|
||||
designSettingsPrinterCoupledHint: 'Auf den Drucker abgestimmt, für den die Datei erstellt wurde — auf deinem kann der Wert falsch oder unzulässig sein.',
|
||||
enqueuing: 'Slice-Auftrag wird übermittelt…',
|
||||
queued: 'In Warteschlange…',
|
||||
failed: 'Slicen fehlgeschlagen. Logs des Slicer-Sidecars prüfen.',
|
||||
|
|
|
|||
|
|
@ -4313,6 +4313,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Default',
|
||||
fromFile: 'from file',
|
||||
fromFileHint: 'The designer changed this in the source file. Its value is {{value}}.',
|
||||
fromFilePrinterCoupled: "designer's printer",
|
||||
fromFilePrinterCoupledHint: 'Tuned for the printer this file was designed for -- may be wrong or out of range on yours.',
|
||||
useFromFile: "Use the source file's value for {{option}}",
|
||||
otherFromFile: 'Other settings from this file',
|
||||
loading: 'Loading slicer settings…',
|
||||
mode: {
|
||||
simple: 'Simple',
|
||||
|
|
@ -4325,6 +4332,12 @@ export default {
|
|||
noMatches: 'No settings match this search.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'not set',
|
||||
processSettingsEmbedded: 'Not used while "Use the file\'s built-in settings" is on -- the file\'s own settings drive this slice.',
|
||||
processSettingsInactive: 'Inactive',
|
||||
presetsHidden: '{{count}} hidden',
|
||||
showAllPresets: 'Show all',
|
||||
showFewerPresets: 'Show fewer',
|
||||
processSettings: 'Process settings',
|
||||
processSettingsHint: "Adjust the picked preset for this slice. Anything you don't touch stays as the preset defines it.",
|
||||
processSettingsChanged: '{{count}} changed',
|
||||
|
|
@ -4358,11 +4371,6 @@ export default {
|
|||
autoOrientHint: 'Let the slicer turn each object onto its best printing side first. Overrides the way the model was laid down in the file.',
|
||||
autoArrange: 'Auto-arrange on the plate',
|
||||
autoArrangeHint: 'Let the slicer position the objects so they no longer overlap. Replaces the layout the file came with.',
|
||||
designSettings: 'Keep the designer\'s settings',
|
||||
designSettingsHint: 'This file changes {{count}} print setting(s) from the stock profile.',
|
||||
designSettingsSelected: '{{selected}} of {{total}} selected',
|
||||
designSettingsPrinterCoupled: 'printer-specific',
|
||||
designSettingsPrinterCoupledHint: 'Tuned for the printer this file was designed for — may be wrong or out of range on yours.',
|
||||
enqueuing: 'Submitting slice job…',
|
||||
queued: 'Queued…',
|
||||
failed: 'Slicing failed. Check the slicer sidecar logs.',
|
||||
|
|
|
|||
|
|
@ -4281,6 +4281,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Predeterminado',
|
||||
fromFile: 'del archivo',
|
||||
fromFileHint: 'El diseñador cambió esto en el archivo de origen. Su valor es {{value}}.',
|
||||
fromFilePrinterCoupled: 'impresora del diseñador',
|
||||
fromFilePrinterCoupledHint: 'Ajustado para la impresora para la que se diseñó este archivo: en la tuya puede ser incorrecto o estar fuera de rango.',
|
||||
useFromFile: 'Usar el valor del archivo de origen para {{option}}',
|
||||
otherFromFile: 'Otros ajustes de este archivo',
|
||||
loading: 'Cargando ajustes del laminador…',
|
||||
mode: {
|
||||
simple: 'Simple',
|
||||
|
|
@ -4293,6 +4300,12 @@ export default {
|
|||
noMatches: 'Ningún ajuste coincide con esta búsqueda.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'sin definir',
|
||||
processSettingsEmbedded: 'No se usa mientras «Usar los ajustes integrados del archivo» está activo: los ajustes del propio archivo rigen este corte.',
|
||||
processSettingsInactive: 'Inactivo',
|
||||
presetsHidden: '{{count}} ocultos',
|
||||
showAllPresets: 'Mostrar todos',
|
||||
showFewerPresets: 'Mostrar menos',
|
||||
processSettings: 'Ajustes de proceso',
|
||||
processSettingsHint: 'Ajusta el perfil seleccionado para este corte. Todo lo que no toques se mantiene como lo define el perfil.',
|
||||
processSettingsChanged: '{{count}} cambiados',
|
||||
|
|
@ -4326,11 +4339,6 @@ export default {
|
|||
autoOrientHint: 'El laminador gira cada objeto hacia su mejor cara de impresión antes de laminar. Sustituye la orientación del archivo.',
|
||||
autoArrange: 'Organizar automáticamente en la base',
|
||||
autoArrangeHint: 'El laminador coloca los objetos para que dejen de solaparse. Sustituye la disposición del archivo.',
|
||||
designSettings: 'Mantener los ajustes del diseñador',
|
||||
designSettingsHint: 'Este archivo cambia {{count}} ajuste(s) de impresión respecto al perfil estándar.',
|
||||
designSettingsSelected: '{{selected}} de {{total}} seleccionados',
|
||||
designSettingsPrinterCoupled: 'específico de la impresora',
|
||||
designSettingsPrinterCoupledHint: 'Ajustado para la impresora para la que se diseñó el archivo: puede ser incorrecto o quedar fuera de rango en la tuya.',
|
||||
enqueuing: 'Enviando el trabajo de laminado…',
|
||||
queued: 'En cola…',
|
||||
failed: 'Error al laminar. Consulte los registros del contenedor auxiliar del laminador.',
|
||||
|
|
|
|||
|
|
@ -4268,6 +4268,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Par défaut',
|
||||
fromFile: 'du fichier',
|
||||
fromFileHint: 'Le concepteur a modifié ce paramètre dans le fichier source. Sa valeur est {{value}}.',
|
||||
fromFilePrinterCoupled: 'imprimante du concepteur',
|
||||
fromFilePrinterCoupledHint: "Réglé pour l'imprimante pour laquelle ce fichier a été conçu — peut être incorrect ou hors plage sur la vôtre.",
|
||||
useFromFile: 'Utiliser la valeur du fichier source pour {{option}}',
|
||||
otherFromFile: 'Autres paramètres de ce fichier',
|
||||
loading: 'Chargement des paramètres du trancheur…',
|
||||
mode: {
|
||||
simple: 'Simple',
|
||||
|
|
@ -4280,6 +4287,12 @@ export default {
|
|||
noMatches: 'Aucun paramètre ne correspond à cette recherche.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'non défini',
|
||||
processSettingsEmbedded: 'Inutilisé tant que « Utiliser les paramètres intégrés du fichier » est activé : ce sont les paramètres du fichier qui pilotent ce découpage.',
|
||||
processSettingsInactive: 'Inactif',
|
||||
presetsHidden: '{{count}} masqués',
|
||||
showAllPresets: 'Tout afficher',
|
||||
showFewerPresets: 'Afficher moins',
|
||||
processSettings: 'Paramètres de process',
|
||||
processSettingsHint: 'Ajustez le profil choisi pour ce découpage. Tout ce que vous ne modifiez pas reste tel que défini par le profil.',
|
||||
processSettingsChanged: '{{count}} modifiés',
|
||||
|
|
@ -4313,11 +4326,6 @@ export default {
|
|||
autoOrientHint: "Le trancheur fait pivoter chaque objet sur sa meilleure face d'impression avant de trancher. Remplace l'orientation enregistrée dans le fichier.",
|
||||
autoArrange: 'Disposer automatiquement sur le plateau',
|
||||
autoArrangeHint: "Le trancheur place les objets pour qu'ils ne se chevauchent plus. Remplace la disposition du fichier.",
|
||||
designSettings: 'Conserver les réglages du concepteur',
|
||||
designSettingsHint: 'Ce fichier modifie {{count}} réglage(s) d\'impression par rapport au profil standard.',
|
||||
designSettingsSelected: '{{selected}} sur {{total}} sélectionnés',
|
||||
designSettingsPrinterCoupled: 'spécifique à l\'imprimante',
|
||||
designSettingsPrinterCoupledHint: 'Réglé pour l\'imprimante visée par le fichier — peut être incorrect ou hors plage sur la vôtre.',
|
||||
enqueuing: 'Envoi du travail de découpage…',
|
||||
queued: 'En file d\'attente…',
|
||||
failed: 'Échec du découpage. Vérifiez les journaux du sidecar.',
|
||||
|
|
|
|||
|
|
@ -4267,6 +4267,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Predefinito',
|
||||
fromFile: 'dal file',
|
||||
fromFileHint: 'Il designer ha modificato questo parametro nel file di origine. Il valore è {{value}}.',
|
||||
fromFilePrinterCoupled: 'stampante del designer',
|
||||
fromFilePrinterCoupledHint: 'Tarato per la stampante per cui è stato progettato questo file: sulla tua può essere errato o fuori intervallo.',
|
||||
useFromFile: 'Usa il valore del file di origine per {{option}}',
|
||||
otherFromFile: 'Altre impostazioni da questo file',
|
||||
loading: 'Caricamento impostazioni dello slicer…',
|
||||
mode: {
|
||||
simple: 'Semplice',
|
||||
|
|
@ -4279,6 +4286,12 @@ export default {
|
|||
noMatches: 'Nessuna impostazione corrisponde a questa ricerca.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'non impostato',
|
||||
processSettingsEmbedded: 'Non utilizzato finché «Usa le impostazioni integrate del file» è attivo: sono le impostazioni del file a guidare questo slice.',
|
||||
processSettingsInactive: 'Non attivo',
|
||||
presetsHidden: '{{count}} nascosti',
|
||||
showAllPresets: 'Mostra tutti',
|
||||
showFewerPresets: 'Mostra meno',
|
||||
processSettings: 'Impostazioni di processo',
|
||||
processSettingsHint: 'Regola il profilo scelto per questo slice. Tutto ciò che non tocchi resta come definito dal profilo.',
|
||||
processSettingsChanged: '{{count}} modificate',
|
||||
|
|
@ -4312,11 +4325,6 @@ export default {
|
|||
autoOrientHint: "Lo slicer ruota ogni oggetto sul lato che si stampa meglio prima di affettare. Sostituisce l'orientamento salvato nel file.",
|
||||
autoArrange: 'Disponi automaticamente sul piatto',
|
||||
autoArrangeHint: 'Lo slicer dispone gli oggetti in modo che non si sovrappongano più. Sostituisce la disposizione del file.',
|
||||
designSettings: 'Mantieni le impostazioni del progettista',
|
||||
designSettingsHint: 'Questo file modifica {{count}} impostazione/i di stampa rispetto al profilo standard.',
|
||||
designSettingsSelected: '{{selected}} di {{total}} selezionate',
|
||||
designSettingsPrinterCoupled: 'specifico della stampante',
|
||||
designSettingsPrinterCoupledHint: 'Tarato per la stampante per cui è stato progettato il file: sulla tua può essere errato o fuori intervallo.',
|
||||
enqueuing: 'Invio lavoro di slicing…',
|
||||
queued: 'In coda…',
|
||||
failed: 'Slicing fallito. Controlla i log del sidecar.',
|
||||
|
|
|
|||
|
|
@ -4279,6 +4279,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: '既定',
|
||||
fromFile: 'ファイル由来',
|
||||
fromFileHint: 'この項目は元ファイルで設計者が変更しています。値は {{value}} です。',
|
||||
fromFilePrinterCoupled: '設計者のプリンター',
|
||||
fromFilePrinterCoupledHint: 'このファイルが設計されたプリンター向けの値です。お使いのプリンターでは不適切または範囲外になる場合があります。',
|
||||
useFromFile: '{{option}} に元ファイルの値を使用する',
|
||||
otherFromFile: 'このファイルのその他の設定',
|
||||
loading: 'スライサー設定を読み込んでいます…',
|
||||
mode: {
|
||||
simple: 'シンプル',
|
||||
|
|
@ -4291,6 +4298,12 @@ export default {
|
|||
noMatches: 'この検索に一致する設定はありません。',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: '未設定',
|
||||
processSettingsEmbedded: '「ファイル内蔵の設定を使用」が有効な間は使用されません。このスライスはファイル自身の設定で実行されます。',
|
||||
processSettingsInactive: '無効',
|
||||
presetsHidden: '{{count}} 件を非表示',
|
||||
showAllPresets: 'すべて表示',
|
||||
showFewerPresets: '表示を減らす',
|
||||
processSettings: 'プロセス設定',
|
||||
processSettingsHint: 'このスライス用に選択したプリセットを調整します。変更しない項目はプリセットの定義のままです。',
|
||||
processSettingsChanged: '{{count}} 件変更',
|
||||
|
|
@ -4324,11 +4337,6 @@ export default {
|
|||
autoOrientHint: 'スライスする前に、各オブジェクトを印刷に適した面へ自動で回転させます。ファイルに保存された向きは上書きされます。',
|
||||
autoArrange: 'プレート上に自動配置',
|
||||
autoArrangeHint: 'オブジェクトが重ならないようにスライサーが並べ直します。ファイルの配置は置き換えられます。',
|
||||
designSettings: '設計者の設定を保持',
|
||||
designSettingsHint: 'このファイルは標準プロファイルから {{count}} 個の印刷設定を変更しています。',
|
||||
designSettingsSelected: '{{total}} 個中 {{selected}} 個を選択',
|
||||
designSettingsPrinterCoupled: 'プリンター固有',
|
||||
designSettingsPrinterCoupledHint: 'このファイルが対象とするプリンター向けに調整された値です。お使いのプリンターでは不適切または範囲外になる場合があります。',
|
||||
enqueuing: 'スライスジョブを送信中…',
|
||||
queued: '待機中…',
|
||||
failed: 'スライスに失敗。サイドカーのログを確認してください。',
|
||||
|
|
|
|||
|
|
@ -4070,6 +4070,13 @@ export default {
|
|||
},
|
||||
},
|
||||
slicerSettings: {
|
||||
filamentDefault: '기본값',
|
||||
fromFile: '파일에서',
|
||||
fromFileHint: '디자이너가 원본 파일에서 이 항목을 변경했습니다. 값은 {{value}}입니다.',
|
||||
fromFilePrinterCoupled: '디자이너의 프린터',
|
||||
fromFilePrinterCoupledHint: '이 파일이 설계된 프린터에 맞춘 값입니다. 사용 중인 프린터에서는 잘못되거나 범위를 벗어날 수 있습니다.',
|
||||
useFromFile: '{{option}}에 원본 파일의 값 사용',
|
||||
otherFromFile: '이 파일의 기타 설정',
|
||||
loading: '슬라이서 설정을 불러오는 중…',
|
||||
mode: {
|
||||
simple: '간단',
|
||||
|
|
@ -4082,6 +4089,12 @@ export default {
|
|||
noMatches: '검색과 일치하는 설정이 없습니다.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: '미설정',
|
||||
processSettingsEmbedded: "'파일에 포함된 설정 사용'이 켜져 있는 동안에는 사용되지 않습니다. 이 슬라이스는 파일 자체의 설정을 따릅니다.",
|
||||
processSettingsInactive: '비활성',
|
||||
presetsHidden: '{{count}}개 숨김',
|
||||
showAllPresets: '모두 표시',
|
||||
showFewerPresets: '간략히 표시',
|
||||
processSettings: '프로세스 설정',
|
||||
processSettingsHint: '이 슬라이스에 사용할 프리셋을 조정합니다. 건드리지 않은 항목은 프리셋 정의를 그대로 따릅니다.',
|
||||
processSettingsChanged: '{{count}}개 변경됨',
|
||||
|
|
@ -4111,11 +4124,6 @@ export default {
|
|||
autoOrientHint: '슬라이싱하기 전에 각 개체를 출력하기 좋은 면으로 회전시킵니다. 파일에 저장된 방향을 덮어씁니다.',
|
||||
autoArrange: '플레이트에 자동 배치',
|
||||
autoArrangeHint: '개체가 겹치지 않도록 슬라이서가 다시 배치합니다. 파일의 배치를 대체합니다.',
|
||||
designSettings: '디자이너 설정 유지',
|
||||
designSettingsHint: '이 파일은 기본 프로파일에서 {{count}}개의 출력 설정을 변경합니다.',
|
||||
designSettingsSelected: '{{total}}개 중 {{selected}}개 선택됨',
|
||||
designSettingsPrinterCoupled: '프린터 전용',
|
||||
designSettingsPrinterCoupledHint: '이 파일이 대상으로 한 프린터에 맞춘 값입니다. 사용 중인 프린터에서는 잘못되었거나 범위를 벗어날 수 있습니다.',
|
||||
enqueuing: '슬라이싱 작업 제출 중…',
|
||||
queued: '대기 중…',
|
||||
failed: '슬라이싱 실패. 슬라이서 사이드카 로그를 확인하세요.',
|
||||
|
|
|
|||
|
|
@ -4267,6 +4267,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Padrão',
|
||||
fromFile: 'do arquivo',
|
||||
fromFileHint: 'O designer alterou isto no arquivo de origem. O valor é {{value}}.',
|
||||
fromFilePrinterCoupled: 'impressora do designer',
|
||||
fromFilePrinterCoupledHint: 'Ajustado para a impressora para a qual este arquivo foi projetado — pode estar errado ou fora de faixa na sua.',
|
||||
useFromFile: 'Usar o valor do arquivo de origem para {{option}}',
|
||||
otherFromFile: 'Outras configurações deste arquivo',
|
||||
loading: 'Carregando configurações do fatiador…',
|
||||
mode: {
|
||||
simple: 'Simples',
|
||||
|
|
@ -4279,6 +4286,12 @@ export default {
|
|||
noMatches: 'Nenhuma configuração corresponde a esta busca.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'não definido',
|
||||
processSettingsEmbedded: 'Não é usado enquanto “Usar as configurações internas do arquivo” estiver ativo — as configurações do próprio arquivo conduzem este fatiamento.',
|
||||
processSettingsInactive: 'Inativo',
|
||||
presetsHidden: '{{count}} ocultos',
|
||||
showAllPresets: 'Mostrar todos',
|
||||
showFewerPresets: 'Mostrar menos',
|
||||
processSettings: 'Configurações de processo',
|
||||
processSettingsHint: 'Ajuste o perfil escolhido para este fatiamento. Tudo o que você não alterar permanece como o perfil define.',
|
||||
processSettingsChanged: '{{count}} alterados',
|
||||
|
|
@ -4312,11 +4325,6 @@ export default {
|
|||
autoOrientHint: 'O fatiador gira cada objeto para o lado que imprime melhor antes de fatiar. Substitui a orientação salva no arquivo.',
|
||||
autoArrange: 'Organizar automaticamente na mesa',
|
||||
autoArrangeHint: 'O fatiador posiciona os objetos para que não se sobreponham. Substitui a disposição do arquivo.',
|
||||
designSettings: 'Manter as configurações do designer',
|
||||
designSettingsHint: 'Este arquivo altera {{count}} configuração(ões) de impressão em relação ao perfil padrão.',
|
||||
designSettingsSelected: '{{selected}} de {{total}} selecionadas',
|
||||
designSettingsPrinterCoupled: 'específico da impressora',
|
||||
designSettingsPrinterCoupledHint: 'Ajustado para a impressora para a qual o arquivo foi projetado — pode estar incorreto ou fora de faixa na sua.',
|
||||
enqueuing: 'Enviando trabalho de fatiamento…',
|
||||
queued: 'Na fila…',
|
||||
failed: 'Falha ao fatiar. Verifique os logs do sidecar.',
|
||||
|
|
|
|||
|
|
@ -4062,6 +4062,13 @@ export default {
|
|||
},
|
||||
},
|
||||
slicerSettings: {
|
||||
filamentDefault: 'По умолчанию',
|
||||
fromFile: 'из файла',
|
||||
fromFileHint: 'Автор модели изменил этот параметр в исходном файле. Значение: {{value}}.',
|
||||
fromFilePrinterCoupled: 'принтер автора',
|
||||
fromFilePrinterCoupledHint: 'Подобрано под принтер, для которого создан файл, — на вашем значение может быть неверным или вне диапазона.',
|
||||
useFromFile: 'Использовать значение из исходного файла для «{{option}}»',
|
||||
otherFromFile: 'Другие параметры из этого файла',
|
||||
loading: 'Загрузка настроек слайсера…',
|
||||
mode: {
|
||||
simple: 'Простой',
|
||||
|
|
@ -4074,6 +4081,12 @@ export default {
|
|||
noMatches: 'Нет параметров, соответствующих запросу.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'не задано',
|
||||
processSettingsEmbedded: 'Не используется, пока включено «Использовать встроенные настройки файла» — нарезкой управляют настройки самого файла.',
|
||||
processSettingsInactive: 'Не активно',
|
||||
presetsHidden: 'скрыто: {{count}}',
|
||||
showAllPresets: 'Показать все',
|
||||
showFewerPresets: 'Показать меньше',
|
||||
processSettings: 'Параметры процесса',
|
||||
processSettingsHint: 'Настройте выбранный профиль для этой нарезки. Всё, что вы не измените, останется как задано в профиле.',
|
||||
processSettingsChanged: 'изменено: {{count}}',
|
||||
|
|
@ -4107,11 +4120,6 @@ export default {
|
|||
autoOrientHint: 'Слайсер повернёт каждый объект на сторону, которая печатается лучше всего. Ориентация из файла будет заменена.',
|
||||
autoArrange: 'Автоматически разместить на столе',
|
||||
autoArrangeHint: 'Слайсер расставит объекты так, чтобы они не перекрывались. Расположение из файла будет заменено.',
|
||||
designSettings: "Сохранить настройки автора",
|
||||
designSettingsHint: "Этот файл меняет {{count}} настроек печати по сравнению со стандартным профилем.",
|
||||
designSettingsSelected: "Выбрано {{selected}} из {{total}}",
|
||||
designSettingsPrinterCoupled: "зависит от принтера",
|
||||
designSettingsPrinterCoupledHint: "Значение подобрано для принтера, под который создан файл, — на вашем оно может быть неверным или вне допустимого диапазона.",
|
||||
enqueuing: "Отправка задания на нарезку…",
|
||||
queued: "В очереди…",
|
||||
failed: "Ошибка нарезки. Проверьте журналы вспомогательного сервиса слайсера.",
|
||||
|
|
|
|||
|
|
@ -4268,6 +4268,13 @@ export default {
|
|||
|
||||
// Dilimle (SliceModal ile slicer-API entegrasyonu)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'Varsayılan',
|
||||
fromFile: 'dosyadan',
|
||||
fromFileHint: 'Tasarımcı bunu kaynak dosyada değiştirdi. Değeri {{value}}.',
|
||||
fromFilePrinterCoupled: 'tasarımcının yazıcısı',
|
||||
fromFilePrinterCoupledHint: 'Bu dosyanın tasarlandığı yazıcıya göre ayarlanmıştır; sizinkinde yanlış veya aralık dışı olabilir.',
|
||||
useFromFile: '{{option}} için kaynak dosyadaki değeri kullan',
|
||||
otherFromFile: 'Bu dosyadaki diğer ayarlar',
|
||||
loading: 'Dilimleyici ayarları yükleniyor…',
|
||||
mode: {
|
||||
simple: 'Basit',
|
||||
|
|
@ -4280,6 +4287,12 @@ export default {
|
|||
noMatches: 'Bu aramayla eşleşen ayar yok.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'ayarlanmadı',
|
||||
processSettingsEmbedded: '“Dosyanın yerleşik ayarlarını kullan” açıkken kullanılmaz — bu dilimlemeyi dosyanın kendi ayarları yönetir.',
|
||||
processSettingsInactive: 'Etkin değil',
|
||||
presetsHidden: '{{count}} gizli',
|
||||
showAllPresets: 'Tümünü göster',
|
||||
showFewerPresets: 'Daha az göster',
|
||||
processSettings: 'İşlem ayarları',
|
||||
processSettingsHint: 'Seçilen ön ayarı bu dilimleme için düzenleyin. Dokunmadığınız her şey ön ayardaki gibi kalır.',
|
||||
processSettingsChanged: '{{count}} değişti',
|
||||
|
|
@ -4313,11 +4326,6 @@ export default {
|
|||
autoOrientHint: 'Dilimleyici, dilimlemeden önce her nesneyi en iyi basılan yüzüne çevirir. Dosyada kayıtlı yönlendirmenin yerini alır.',
|
||||
autoArrange: 'Tablaya otomatik yerleştir',
|
||||
autoArrangeHint: 'Dilimleyici nesneleri üst üste binmeyecek şekilde yerleştirir. Dosyadaki yerleşimin yerini alır.',
|
||||
designSettings: 'Tasarımcının ayarlarını koru',
|
||||
designSettingsHint: 'Bu dosya standart profile göre {{count}} baskı ayarını değiştiriyor.',
|
||||
designSettingsSelected: '{{total}} ayardan {{selected}} tanesi seçili',
|
||||
designSettingsPrinterCoupled: 'yazıcıya özel',
|
||||
designSettingsPrinterCoupledHint: 'Dosyanın tasarlandığı yazıcıya göre ayarlanmış — sizinkinde yanlış veya aralık dışı olabilir.',
|
||||
enqueuing: 'Dilimleme işi gönderiliyor…',
|
||||
queued: 'Kuyrukta…',
|
||||
failed: 'Dilimleme başarısız. Dilimleyici yardımcı bileşen günlüklerini kontrol edin.',
|
||||
|
|
|
|||
|
|
@ -4312,6 +4312,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: 'За замовчуванням',
|
||||
fromFile: 'з файлу',
|
||||
fromFileHint: 'Автор моделі змінив цей параметр у вихідному файлі. Значення: {{value}}.',
|
||||
fromFilePrinterCoupled: 'принтер автора',
|
||||
fromFilePrinterCoupledHint: 'Підібрано під принтер, для якого створено файл, — на вашому значення може бути хибним або поза діапазоном.',
|
||||
useFromFile: 'Використовувати значення з вихідного файлу для «{{option}}»',
|
||||
otherFromFile: 'Інші параметри з цього файлу',
|
||||
loading: 'Завантаження налаштувань слайсера…',
|
||||
mode: {
|
||||
simple: 'Простий',
|
||||
|
|
@ -4324,6 +4331,12 @@ export default {
|
|||
noMatches: 'Немає параметрів, що відповідають запиту.',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: 'не задано',
|
||||
processSettingsEmbedded: 'Не використовується, доки увімкнено «Використовувати вбудовані параметри файлу» — нарізанням керують параметри самого файлу.',
|
||||
processSettingsInactive: 'Неактивно',
|
||||
presetsHidden: 'приховано: {{count}}',
|
||||
showAllPresets: 'Показати всі',
|
||||
showFewerPresets: 'Показати менше',
|
||||
processSettings: 'Параметри процесу',
|
||||
processSettingsHint: 'Налаштуйте вибраний профіль для цього нарізання. Усе, чого ви не змінили, лишається як визначено профілем.',
|
||||
processSettingsChanged: 'змінено: {{count}}',
|
||||
|
|
@ -4357,11 +4370,6 @@ export default {
|
|||
autoOrientHint: "Слайсер поверне кожен об'єкт на бік, який друкується найкраще. Орієнтацію з файлу буде замінено.",
|
||||
autoArrange: 'Автоматично розмістити на столі',
|
||||
autoArrangeHint: "Слайсер розставить об'єкти так, щоб вони не перекривалися. Розташування з файлу буде замінено.",
|
||||
designSettings: "Зберегти налаштування автора",
|
||||
designSettingsHint: "Цей файл змінює {{count}} налаштувань друку порівняно зі стандартним профілем.",
|
||||
designSettingsSelected: "Вибрано {{selected}} із {{total}}",
|
||||
designSettingsPrinterCoupled: "залежить від принтера",
|
||||
designSettingsPrinterCoupledHint: "Значення підібрано для принтера, під який створено файл, — на вашому воно може бути неправильним або поза допустимим діапазоном.",
|
||||
enqueuing: "Надсилання завдання нарізання…",
|
||||
queued: "У черзі…",
|
||||
failed: "Не вдалося виконати нарізання. Перевірте журнали слайсера.",
|
||||
|
|
|
|||
|
|
@ -4267,6 +4267,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: '默认',
|
||||
fromFile: '来自文件',
|
||||
fromFileHint: '设计者在源文件中修改了此项,其值为 {{value}}。',
|
||||
fromFilePrinterCoupled: '设计者的打印机',
|
||||
fromFilePrinterCoupledHint: '针对该文件设计时所用的打印机调校,在你的打印机上可能不正确或超出范围。',
|
||||
useFromFile: '对 {{option}} 使用源文件中的值',
|
||||
otherFromFile: '此文件中的其他设置',
|
||||
loading: '正在加载切片设置…',
|
||||
mode: {
|
||||
simple: '简单',
|
||||
|
|
@ -4279,6 +4286,12 @@ export default {
|
|||
noMatches: '没有与搜索匹配的设置。',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: '未设置',
|
||||
processSettingsEmbedded: '启用“使用文件内置设置”时不生效——本次切片由文件自身的设置决定。',
|
||||
processSettingsInactive: '未启用',
|
||||
presetsHidden: '已隐藏 {{count}} 项',
|
||||
showAllPresets: '显示全部',
|
||||
showFewerPresets: '显示较少',
|
||||
processSettings: '工艺设置',
|
||||
processSettingsHint: '为本次切片调整所选预设。未改动的项目仍按预设定义。',
|
||||
processSettingsChanged: '已更改 {{count}} 项',
|
||||
|
|
@ -4312,11 +4325,6 @@ export default {
|
|||
autoOrientHint: '切片前由切片器把每个模型转到最适合打印的一面,会覆盖文件中保存的朝向。',
|
||||
autoArrange: '自动排布在热床上',
|
||||
autoArrangeHint: '由切片器重新摆放模型,使其不再重叠,会替换文件自带的布局。',
|
||||
designSettings: '保留设计者的设置',
|
||||
designSettingsHint: '此文件相对标准配置修改了 {{count}} 项打印设置。',
|
||||
designSettingsSelected: '已选择 {{selected}} / {{total}}',
|
||||
designSettingsPrinterCoupled: '与打印机相关',
|
||||
designSettingsPrinterCoupledHint: '该值是为此文件面向的打印机调校的,在你的打印机上可能不正确或超出范围。',
|
||||
enqueuing: '提交切片任务中…',
|
||||
queued: '已排队…',
|
||||
failed: '切片失败。请检查切片器 sidecar 日志。',
|
||||
|
|
|
|||
|
|
@ -4267,6 +4267,13 @@ export default {
|
|||
|
||||
// Slice (slicer-API integration via SliceModal)
|
||||
slicerSettings: {
|
||||
filamentDefault: '預設',
|
||||
fromFile: '來自檔案',
|
||||
fromFileHint: '設計者在來源檔案中修改了此項,其值為 {{value}}。',
|
||||
fromFilePrinterCoupled: '設計者的印表機',
|
||||
fromFilePrinterCoupledHint: '針對該檔案設計時所用的印表機調校,在你的印表機上可能不正確或超出範圍。',
|
||||
useFromFile: '對 {{option}} 使用來源檔案中的值',
|
||||
otherFromFile: '此檔案中的其他設定',
|
||||
loading: '正在載入切片設定…',
|
||||
mode: {
|
||||
simple: '簡易',
|
||||
|
|
@ -4279,6 +4286,12 @@ export default {
|
|||
noMatches: '沒有符合搜尋的設定。',
|
||||
},
|
||||
slice: {
|
||||
filamentSlotUnset: '未設定',
|
||||
processSettingsEmbedded: '啟用「使用檔案內建設定」時不生效——本次切片由檔案自身的設定決定。',
|
||||
processSettingsInactive: '未啟用',
|
||||
presetsHidden: '已隱藏 {{count}} 項',
|
||||
showAllPresets: '顯示全部',
|
||||
showFewerPresets: '顯示較少',
|
||||
processSettings: '列印參數',
|
||||
processSettingsHint: '為本次切片調整所選預設。未變更的項目仍依預設定義。',
|
||||
processSettingsChanged: '已變更 {{count}} 項',
|
||||
|
|
@ -4312,11 +4325,6 @@ export default {
|
|||
autoOrientHint: '切片前由切片器將每個模型轉到最適合列印的一面,會覆蓋檔案中儲存的朝向。',
|
||||
autoArrange: '自動排列在熱床上',
|
||||
autoArrangeHint: '由切片器重新擺放模型,使其不再重疊,會取代檔案自帶的版面配置。',
|
||||
designSettings: '保留設計者的設定',
|
||||
designSettingsHint: '此檔案相對標準設定檔修改了 {{count}} 項列印設定。',
|
||||
designSettingsSelected: '已選擇 {{selected}} / {{total}}',
|
||||
designSettingsPrinterCoupled: '與印表機相關',
|
||||
designSettingsPrinterCoupledHint: '此值是為該檔案面向的印表機調校的,在你的印表機上可能不正確或超出範圍。',
|
||||
enqueuing: '提交切片任務中…',
|
||||
queued: '已排隊…',
|
||||
failed: '切片失敗。請檢查切片器 sidecar 日誌。',
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ const VECTOR_TYPES = new Set(['coBools', 'coFloats', 'coFloatsOrPercents']);
|
|||
export const isVectorOption = (option: ProcessOption): boolean => VECTOR_TYPES.has(option.type);
|
||||
|
||||
/**
|
||||
* Numeric bound from the schema, or `undefined` when the extractor left an
|
||||
* unparsed C++ literal behind (`"0.3f"`, `"def_infill_anchor_min->sidetext"`).
|
||||
* Numeric bound from the schema, or `undefined` when it isn't a number at all.
|
||||
* Float literals are normalised by the generator, but a handful of bounds are
|
||||
* unresolved C++ expressions the extractor could not follow, and those must not
|
||||
* reach an input's `min`/`max`.
|
||||
*/
|
||||
export function numericBound(bound: number | string | undefined): number | undefined {
|
||||
if (typeof bound === 'number') return Number.isFinite(bound) ? bound : undefined;
|
||||
|
|
@ -43,11 +45,10 @@ export function displaySidetext(option: ProcessOption): string | undefined {
|
|||
export function defaultForDisplay(option: ProcessOption): string {
|
||||
const d = option.default;
|
||||
if (d === undefined) return '';
|
||||
if (Array.isArray(d)) {
|
||||
// A handful of defaults were mis-extracted from C++ float literals —
|
||||
// `0.f` became [0, "f"]. Drop the stray suffix token rather than render it.
|
||||
return d.filter((v) => v !== 'f').map(String).join(', ');
|
||||
}
|
||||
// Per-extruder vectors render as a comma-separated list. C++ literal
|
||||
// artefacts (`0.`, `0.3f`, `100.%`) are normalised by
|
||||
// scripts/generate-slicer-schema.mjs, so nothing needs unpicking here.
|
||||
if (Array.isArray(d)) return d.map(String).join(', ');
|
||||
if (typeof d === 'boolean') return d ? '1' : '0';
|
||||
return String(d);
|
||||
}
|
||||
|
|
@ -106,7 +107,7 @@ export function isModified(option: ProcessOption, value: SettingValue | undefine
|
|||
const d = option.default;
|
||||
if (d === undefined) return asString !== '';
|
||||
|
||||
const defaultSerialized = serializeSetting(option, Array.isArray(d) ? d.filter((v) => v !== 'f').map(String).join(', ') : (d as SettingValue));
|
||||
const defaultSerialized = serializeSetting(option, Array.isArray(d) ? d.map(String).join(', ') : (d as SettingValue));
|
||||
const defaultString = Array.isArray(defaultSerialized) ? defaultSerialized.join(', ') : defaultSerialized;
|
||||
|
||||
return asString !== defaultString;
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ export interface ProcessOption {
|
|||
/** Unit shown after the input ("mm", "mm/s²", "%"). */
|
||||
sidetext?: string;
|
||||
/**
|
||||
* Bounds as the extractor found them. Usually numeric, but a few carry
|
||||
* unparsed C++ float literals ("0.3f") — callers must coerce and ignore what
|
||||
* doesn't convert.
|
||||
* Bounds, normalised out of their C++ source form by the generator. Typed as
|
||||
* string too because the JSON carries both shapes; coerce with
|
||||
* ``numericBound`` rather than assuming a number.
|
||||
*/
|
||||
min?: number | string;
|
||||
max?: number | string;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@
|
|||
// derived from the backend's canonical PRINTER_MODEL_MAP (fetched via
|
||||
// /slicer/printer-models), not duplicated here.
|
||||
//
|
||||
// The result drives grouping, not hard hiding: a preset no rule covers
|
||||
// stays in the main list, and only a preset that resolves to a *different*
|
||||
// printer is pushed into an "Other printers" group.
|
||||
// Only a definite 'mismatch' is acted on: the dropdown holds those back
|
||||
// behind a "Show all" link. A preset no rule covers classifies as 'unknown'
|
||||
// and always stays in the list — absence of evidence is not evidence of
|
||||
// incompatibility, and hiding an untagged preset would make a user's own
|
||||
// imported profiles disappear. That asymmetry is why every parse failure
|
||||
// below returns 'unknown' rather than guessing a mismatch.
|
||||
|
||||
export type PrinterCompatibility = 'match' | 'mismatch' | 'unknown';
|
||||
|
||||
|
|
@ -100,6 +103,29 @@ function normalizeModelFragment(s: string): string {
|
|||
return s.replace(/\s+/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop BambuStudio's ``"# "`` user-clone prefix.
|
||||
*
|
||||
* Editing a system preset saves a copy named ``"# Bambu Lab X1 Carbon 0.4
|
||||
* nozzle"``, and `.bbscfg` bundle exports use the same convention. Two places
|
||||
* need it off:
|
||||
*
|
||||
* 1. ``extractPrinterPresetModel`` — the prefix fails its "Bambu Lab …"
|
||||
* test, so a cloned *printer* made every preset classify as 'unknown'
|
||||
* and the dropdown filter silently did nothing.
|
||||
* 2. The ``compatible_printers`` comparison, where a prefix on one side
|
||||
* alone reads as a mismatch against the very printer the preset was
|
||||
* cloned from — and a mismatch now hides the preset.
|
||||
*
|
||||
* The ``@`` tag extractors need no such handling: they scan for "@BBL " or
|
||||
* the last "@", both of which skip a leading prefix already.
|
||||
*
|
||||
* The backend normalises the same prefix in ``_canonical_printer_model``.
|
||||
*/
|
||||
function stripUserClonePrefix(name: string): string {
|
||||
return name.replace(/^#\s*/, '').trim();
|
||||
}
|
||||
|
||||
// Bambu Studio's naming convention for bundled presets: the 0.4 nozzle is
|
||||
// the default and its variants drop the nozzle suffix; 0.2 / 0.6 / 0.8
|
||||
// carry an explicit "<size> nozzle" segment. So a process with no suffix
|
||||
|
|
@ -132,7 +158,7 @@ function extractBblToken(presetName: string): { token: string; nozzle: string |
|
|||
// nozzle]" printer preset name. Returns null for non-Bambu printer
|
||||
// presets — there is no reliable name-based match against those.
|
||||
function extractPrinterPresetModel(printerPresetName: string): { model: string; nozzle: string | null } | null {
|
||||
const m = printerPresetName.match(/^Bambu Lab\s+(.+)$/i);
|
||||
const m = stripUserClonePrefix(printerPresetName).match(/^Bambu Lab\s+(.+)$/i);
|
||||
if (!m) return null;
|
||||
const { stripped, nozzle } = takeNozzleSuffix(m[1]);
|
||||
return stripped ? { model: stripped, nozzle } : null;
|
||||
|
|
@ -282,7 +308,12 @@ export function presetCompatibility(
|
|||
// authoritative when set.
|
||||
const compat = preset.compatible_printers;
|
||||
if (compat && compat.length > 0) {
|
||||
return compat.includes(selectedPrinterName) ? 'match' : 'mismatch';
|
||||
// Compared with the clone prefix off both sides: a preset cloned from a
|
||||
// system printer lists the *unprefixed* name, and comparing that raw
|
||||
// against a selected "# Bambu Lab …" reads as a mismatch — which now
|
||||
// hides the preset rather than merely demoting it.
|
||||
const selected = stripUserClonePrefix(selectedPrinterName);
|
||||
return compat.some((name) => stripUserClonePrefix(name) === selected) ? 'match' : 'mismatch';
|
||||
}
|
||||
// (2) BambuStudio's `@BBL <model>` name convention — covers cloud /
|
||||
// standard presets that don't carry compatible_printers.
|
||||
|
|
|
|||
2
static/assets/index-D4VkH83v.css
Normal file
2
static/assets/index-D4VkH83v.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -34,7 +34,7 @@ The actual bridge flow used is calculated by multiplying this value with the fil
|
|||
Recommended to use with a higher Bridge density or Bridge flow ratio.
|
||||
|
||||
The maximum value is 100% or the nozzle diameter.
|
||||
If set to 0, the line width will match the Internal solid infill width.`,sidetext:`mm or %`,min:0,max:100,default:`100.%`},_={type:`coBool`,mode:`advanced`,label:`Don't support bridges`,tooltip:`This disables supporting bridges, which decreases the amount of support required. Bridges can usually be printed directly without support over a reasonable distance.`,default:!1},v={type:`coFloat`,mode:`advanced`,label:`Brim ear detection radius`,tooltip:`The geometry will be decimated before detecting sharp angles. This parameter indicates the minimum length of the deviation for the decimation.
|
||||
If set to 0, the line width will match the Internal solid infill width.`,sidetext:`mm or %`,min:0,max:100,default:`100%`},_={type:`coBool`,mode:`advanced`,label:`Don't support bridges`,tooltip:`This disables supporting bridges, which decreases the amount of support required. Bridges can usually be printed directly without support over a reasonable distance.`,default:!1},v={type:`coFloat`,mode:`advanced`,label:`Brim ear detection radius`,tooltip:`The geometry will be decimated before detecting sharp angles. This parameter indicates the minimum length of the deviation for the decimation.
|
||||
0 to deactivate.`,sidetext:`mm`,min:0,default:1},y={type:`coFloat`,mode:`advanced`,label:`Brim ear max angle`,tooltip:`Maximum angle to let a brim ear appear.
|
||||
If set to 0, no brim will be created.
|
||||
If set to ~180, brim will be created on everything but straight sections.`,sidetext:`°`,min:0,max:180,default:125},b={type:`coFloat`,mode:`advanced`,label:`Brim flow ratio`,tooltip:`This factor affects the amount of material for brims.
|
||||
|
|
@ -50,7 +50,7 @@ If your current setup already works well, enabling it may be unnecessary and can
|
|||
- Each Assembly: uses a single shared center for the whole object or assembly.`,enum_values:[`each_surface`,`each_model`,`each_assembly`],enum_labels:[`Each Surface`,`Each Model`,`Each Assembly`],default:`each_surface`},E={type:`coBool`,mode:`advanced`,label:`Combine brims`,tooltip:`Combine multiple brims into one when they are close to each other. This can improve brim adhesion.`,default:!1},D={type:`coEnum`,mode:`advanced`,label:`Bridge counterbore holes`,tooltip:`This option creates bridges for counterbore holes, allowing them to be printed without support. Available modes include:
|
||||
1. None: No bridge is created
|
||||
2. Partially Bridged: Only a part of the unsupported area will be bridged
|
||||
3. Sacrificial Layer: A full sacrificial bridge layer is created`,enum_values:[`none`,`partiallybridge`,`sacrificiallayer`],enum_labels:[`None`,`Partially bridged`,`Sacrificial layer`],default:`none`},O={type:`coFloats`,mode:`advanced`,label:`Normal printing`,tooltip:`This is the default acceleration for both normal printing and travel after the first layer.`,sidetext:`mm/s²`,min:0,default:[500]},k={type:`coFloats`,mode:`advanced`,label:`Default`,tooltip:`Default jerk.`,sidetext:`mm/s`,min:0,default:[0]},A={type:`coFloats`,mode:`advanced`,label:`Junction Deviation`,tooltip:`Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting).`,sidetext:`mm`,min:`0.f`,max:`0.3f`,default:[0,`f`]},j={type:`coBool`,mode:`advanced`,label:`Detect narrow internal solid infills`,tooltip:`This option will auto-detect narrow internal solid infill areas. If enabled, the concentric pattern will be used for the area to speed up printing. Otherwise, the rectilinear pattern will be used by default.`,default:!0},M={type:`coBool`,mode:`advanced`,label:`Detect overhang walls`,tooltip:`This detects the overhang percentage relative to line width and uses a different speed to print. For 100%% overhang, bridging speed is used.`,default:!0},N={type:`coBool`,mode:`advanced`,label:`Detect thin walls`,tooltip:`This detects thin walls which can\\u2019t contain two lines and uses a single line to print. It may not print as well because it\\u2019s not a closed loop.`,default:!1},P={type:`coEnum`,mode:`advanced`,label:`Filter out small internal bridges`,tooltip:`This option can help reduce pillowing on top surfaces in heavily slanted or curved models.
|
||||
3. Sacrificial Layer: A full sacrificial bridge layer is created`,enum_values:[`none`,`partiallybridge`,`sacrificiallayer`],enum_labels:[`None`,`Partially bridged`,`Sacrificial layer`],default:`none`},O={type:`coFloats`,mode:`advanced`,label:`Normal printing`,tooltip:`This is the default acceleration for both normal printing and travel after the first layer.`,sidetext:`mm/s²`,min:0,default:[500]},k={type:`coFloats`,mode:`advanced`,label:`Default`,tooltip:`Default jerk.`,sidetext:`mm/s`,min:0,default:[0]},A={type:`coFloats`,mode:`advanced`,label:`Junction Deviation`,tooltip:`Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting).`,sidetext:`mm`,min:`0`,max:`0.3`,default:[0]},j={type:`coBool`,mode:`advanced`,label:`Detect narrow internal solid infills`,tooltip:`This option will auto-detect narrow internal solid infill areas. If enabled, the concentric pattern will be used for the area to speed up printing. Otherwise, the rectilinear pattern will be used by default.`,default:!0},M={type:`coBool`,mode:`advanced`,label:`Detect overhang walls`,tooltip:`This detects the overhang percentage relative to line width and uses a different speed to print. For 100%% overhang, bridging speed is used.`,default:!0},N={type:`coBool`,mode:`advanced`,label:`Detect thin walls`,tooltip:`This detects thin walls which can\\u2019t contain two lines and uses a single line to print. It may not print as well because it\\u2019s not a closed loop.`,default:!1},P={type:`coEnum`,mode:`advanced`,label:`Filter out small internal bridges`,tooltip:`This option can help reduce pillowing on top surfaces in heavily slanted or curved models.
|
||||
By default, small internal bridges are filtered out and the internal solid infill is printed directly over the sparse infill. This works well in most cases, speeding up printing without too much compromise on top surface quality.
|
||||
However, in heavily slanted or curved models, especially where too low a sparse infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.
|
||||
Enabling limited filtering or no filtering will print internal bridge layer over slightly unsupported internal solid infill. The options below control the sensitivity of the filtering, i.e. they control where internal bridges are created:
|
||||
|
|
@ -96,12 +96,12 @@ Perlin: Perlin noise, which gives a more consistent texture.
|
|||
Billow: Similar to perlin noise, but clumpier.
|
||||
Ridged Multifractal: Ridged noise with sharp, jagged features. Creates marble-like textures.
|
||||
Voronoi: Divides the surface into voronoi cells, and displaces each one by a random amount. Creates a patchwork texture.
|
||||
Ripple: Uniform ripple pattern that ripples left and right of the original path. Repeating pattern, woven appearance.`,enum_values:[`classic`,`perlin`,`billow`,`ridgedmulti`,`voronoi`,`ripple`],enum_labels:[`Classic`,`Perlin`,`Billow`,`Ridged Multifractal`,`Voronoi`,`Ripple`],default:`classic`},ue={type:`coInt`,mode:`advanced`,label:`Fuzzy Skin Noise Octaves`,tooltip:`The number of octaves of coherent noise to use. Higher values increase the detail of the noise, but also increase computation time.`,min:1,max:10,default:4},de={type:`coFloat`,mode:`advanced`,label:`Fuzzy skin noise persistence`,tooltip:`The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise.`,min:`0.01f`,max:1,default:.5},fe={type:`coFloat`,mode:`simple`,label:`Fuzzy skin point distance`,tooltip:`The average distance between the random points introduced on each line segment.`,sidetext:`mm`,min:`0.01f`,max:`5.f`,default:.3},pe={type:`coPercent`,mode:`advanced`,label:`Ripple offset`,tooltip:`Shifts the ripple phase forward along the print path by the specified percentage of a wavelength each layer period.
|
||||
Ripple: Uniform ripple pattern that ripples left and right of the original path. Repeating pattern, woven appearance.`,enum_values:[`classic`,`perlin`,`billow`,`ridgedmulti`,`voronoi`,`ripple`],enum_labels:[`Classic`,`Perlin`,`Billow`,`Ridged Multifractal`,`Voronoi`,`Ripple`],default:`classic`},ue={type:`coInt`,mode:`advanced`,label:`Fuzzy Skin Noise Octaves`,tooltip:`The number of octaves of coherent noise to use. Higher values increase the detail of the noise, but also increase computation time.`,min:1,max:10,default:4},de={type:`coFloat`,mode:`advanced`,label:`Fuzzy skin noise persistence`,tooltip:`The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise.`,min:`0.01`,max:1,default:.5},fe={type:`coFloat`,mode:`simple`,label:`Fuzzy skin point distance`,tooltip:`The average distance between the random points introduced on each line segment.`,sidetext:`mm`,min:`0.01`,max:`5`,default:.3},pe={type:`coPercent`,mode:`advanced`,label:`Ripple offset`,tooltip:`Shifts the ripple phase forward along the print path by the specified percentage of a wavelength each layer period.
|
||||
- 0% keeps every layer identical.
|
||||
- 50% shifts the pattern by half a wavelength, effectively inverting the phase.
|
||||
- 100% shifts the pattern by a full wavelength, returning to the original phase.
|
||||
|
||||
The shift is applied once every number of layers set by Layers between ripple offset, so layers within the same group are printed identically.`,sidetext:`%`,min:0,max:100,default:50},me={type:`coInt`,mode:`advanced`,label:`Number of ripples per layer`,tooltip:`Controls how many full cycles of ripples will be added per layer.`,min:1,default:15},he={type:`coFloat`,mode:`advanced`,label:`Fuzzy skin feature size`,tooltip:`The base size of the coherent noise features, in mm. Higher values will result in larger features.`,sidetext:`mm`,min:`0.1f`,max:500,default:1},ge={type:`coFloat`,mode:`simple`,label:`Fuzzy skin thickness`,tooltip:`The width of jittering: it\\u2019s recommended to keep this lower than the outer wall line width.`,sidetext:`mm`,min:0,max:2,default:.2},_e={type:`coFloat`,mode:`advanced`,label:`Gap fill flow ratio`,tooltip:`This factor affects the amount of material for filling the gaps.
|
||||
The shift is applied once every number of layers set by Layers between ripple offset, so layers within the same group are printed identically.`,sidetext:`%`,min:0,max:100,default:50},me={type:`coInt`,mode:`advanced`,label:`Number of ripples per layer`,tooltip:`Controls how many full cycles of ripples will be added per layer.`,min:1,default:15},he={type:`coFloat`,mode:`advanced`,label:`Fuzzy skin feature size`,tooltip:`The base size of the coherent noise features, in mm. Higher values will result in larger features.`,sidetext:`mm`,min:`0.1`,max:500,default:1},ge={type:`coFloat`,mode:`simple`,label:`Fuzzy skin thickness`,tooltip:`The width of jittering: it\\u2019s recommended to keep this lower than the outer wall line width.`,sidetext:`mm`,min:0,max:2,default:.2},_e={type:`coFloat`,mode:`advanced`,label:`Gap fill flow ratio`,tooltip:`This factor affects the amount of material for filling the gaps.
|
||||
|
||||
The actual gap filling flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},ve={type:`coEnum`,mode:`advanced`,label:`Apply gap fill`,tooltip:`Enables gap fill for the selected solid surfaces. The minimum gap length that will be filled can be controlled from the filter out tiny gaps option below.
|
||||
|
||||
|
|
@ -126,12 +126,12 @@ Set it to 0 or 100% to use the nozzle diameter (for maximum reduction in print t
|
|||
|
||||
The number of layers over which infill is combined is derived by dividing this value with the layer height and rounded down to the nearest decimal.
|
||||
|
||||
Use either absolute mm values (eg. 0.32mm for a 0.4mm nozzle) or % values (eg 80%). This value must not be larger than the nozzle diameter.`,sidetext:`mm or %`,min:0,default:`100.%`},Ne={type:`coFloat`,mode:`advanced`,label:`Sparse infill direction`,tooltip:`This is the angle for sparse infill pattern, which controls the start or main direction of lines.`,sidetext:`°`,min:0,max:360,default:45},Pe={type:`coFloats`,mode:`advanced`,label:`Infill`,tooltip:`Jerk for infill.`,sidetext:`mm/s`,min:0,default:[9]},Fe={type:`coFloat`,mode:`advanced`,label:`Infill lock depth`,tooltip:`The parameter sets the overlapping depth between the interior and skin.`,sidetext:`mm`,min:0,max:100,default:1},Ie={type:`coFloat`,mode:`advanced`,label:`Infill overhang angle`,tooltip:`The angle of the infill angled lines. 60° will result in a pure honeycomb.`,sidetext:`°`,min:15,max:75,default:60},Le={type:`coFloat`,mode:`advanced`,label:`Infill shift step`,tooltip:`This parameter adds a slight displacement to each layer of infill to create a cross texture.`,sidetext:`mm`,min:0,max:10,default:.4},Re={type:`coPercent`,mode:`advanced`,label:`Infill/wall overlap`,tooltip:`This allows the infill area to be enlarged slightly to overlap with walls for better bonding. The percentage value is relative to line width of sparse infill. Set this value to ~10-15% to minimize potential over extrusion and accumulation of material resulting in rough top surfaces.`,sidetext:`%`,default:15},ze={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`This is the printing acceleration for the first layer. Using limited acceleration can improve build plate adhesion.`,sidetext:`mm/s²`,min:0,default:[300]},Be={type:`coFloats`,mode:`advanced`,label:`First layer infill`,tooltip:`This is the speed for solid infill parts of the first layer.`,sidetext:`mm/s`,min:1,default:[60]},Ve={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`Jerk for the first layer.`,sidetext:`mm/s`,min:0,default:[9]},He={type:`coFloatOrPercent`,mode:`advanced`,label:`First layer`,tooltip:`Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},Ue={type:`coPercent`,mode:`advanced`,label:`First layer minimum wall width`,tooltip:`The minimum wall width that should be used for the first layer is recommended to be set to the same size as the nozzle. This adjustment is expected to enhance adhesion.`,sidetext:`%`,min:0,default:85},We={type:`coFloat`,mode:`simple`,label:`First layer height`,tooltip:`Height of the first layer. Making the first layer height thicker can improve build plate adhesion.`,sidetext:`mm`,min:0,default:.2},Ge={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`This is the speed for the first layer except for solid infill sections.`,sidetext:`mm/s`,min:1,default:[30]},Ke={type:`coFloatsOrPercents`,mode:`advanced`,label:`First layer travel`,tooltip:`Travel acceleration of first layer.
|
||||
Use either absolute mm values (eg. 0.32mm for a 0.4mm nozzle) or % values (eg 80%). This value must not be larger than the nozzle diameter.`,sidetext:`mm or %`,min:0,default:`100%`},Ne={type:`coFloat`,mode:`advanced`,label:`Sparse infill direction`,tooltip:`This is the angle for sparse infill pattern, which controls the start or main direction of lines.`,sidetext:`°`,min:0,max:360,default:45},Pe={type:`coFloats`,mode:`advanced`,label:`Infill`,tooltip:`Jerk for infill.`,sidetext:`mm/s`,min:0,default:[9]},Fe={type:`coFloat`,mode:`advanced`,label:`Infill lock depth`,tooltip:`The parameter sets the overlapping depth between the interior and skin.`,sidetext:`mm`,min:0,max:100,default:1},Ie={type:`coFloat`,mode:`advanced`,label:`Infill overhang angle`,tooltip:`The angle of the infill angled lines. 60° will result in a pure honeycomb.`,sidetext:`°`,min:15,max:75,default:60},Le={type:`coFloat`,mode:`advanced`,label:`Infill shift step`,tooltip:`This parameter adds a slight displacement to each layer of infill to create a cross texture.`,sidetext:`mm`,min:0,max:10,default:.4},Re={type:`coPercent`,mode:`advanced`,label:`Infill/wall overlap`,tooltip:`This allows the infill area to be enlarged slightly to overlap with walls for better bonding. The percentage value is relative to line width of sparse infill. Set this value to ~10-15% to minimize potential over extrusion and accumulation of material resulting in rough top surfaces.`,sidetext:`%`,default:15},ze={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`This is the printing acceleration for the first layer. Using limited acceleration can improve build plate adhesion.`,sidetext:`mm/s²`,min:0,default:[300]},Be={type:`coFloats`,mode:`advanced`,label:`First layer infill`,tooltip:`This is the speed for solid infill parts of the first layer.`,sidetext:`mm/s`,min:1,default:[60]},Ve={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`Jerk for the first layer.`,sidetext:`mm/s`,min:0,default:[9]},He={type:`coFloatOrPercent`,mode:`advanced`,label:`First layer`,tooltip:`Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},Ue={type:`coPercent`,mode:`advanced`,label:`First layer minimum wall width`,tooltip:`The minimum wall width that should be used for the first layer is recommended to be set to the same size as the nozzle. This adjustment is expected to enhance adhesion.`,sidetext:`%`,min:0,default:85},We={type:`coFloat`,mode:`simple`,label:`First layer height`,tooltip:`Height of the first layer. Making the first layer height thicker can improve build plate adhesion.`,sidetext:`mm`,min:0,default:.2},Ge={type:`coFloats`,mode:`advanced`,label:`First layer`,tooltip:`This is the speed for the first layer except for solid infill sections.`,sidetext:`mm/s`,min:1,default:[30]},Ke={type:`coFloatsOrPercents`,mode:`advanced`,label:`First layer travel`,tooltip:`Travel acceleration of first layer.
|
||||
The percentage value is relative to Travel Acceleration.`,sidetext:`mm/s² or %`,min:0,default:[`100%`]},qe={type:`coFloatsOrPercents`,mode:`advanced`,label:`First layer travel`,tooltip:`Travel jerk of first layer.
|
||||
The percentage value is relative to Travel Jerk.`,sidetext:`mm/s or %`,min:0,default:[`100%`]},Je={type:`coFloatsOrPercents`,mode:`advanced`,label:`First layer travel speed`,tooltip:`Travel speed of the first layer.`,sidetext:`mm/s or %`,min:1,default:[`100%`]},Ye={type:`coFloats`,mode:`advanced`,label:`Inner wall`,tooltip:`Acceleration of inner walls.`,sidetext:`mm/s²`,min:0,default:[1e4]},Xe={type:`coInt`,mode:`advanced`,label:`Inner walls`,tooltip:`Filament to print inner walls.
|
||||
"Default" uses the active object/part filament.`,min:0,default:0},Ze={type:`coFloat`,mode:`advanced`,label:`Inner wall flow ratio`,tooltip:`This factor affects the amount of material for inner walls.
|
||||
|
||||
The actual inner wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},Qe={type:`coFloats`,mode:`advanced`,label:`Inner wall`,tooltip:`Jerk of inner walls.`,sidetext:`mm/s`,min:0,default:[9]},$e={type:`coFloatOrPercent`,mode:`advanced`,label:`Inner wall`,tooltip:`Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},et={type:`coFloats`,mode:`advanced`,label:`Inner wall`,tooltip:`This is the speed for inner walls.`,sidetext:`mm/s`,min:1,default:[60]},tt={type:`coBool`,mode:`advanced`,label:`Interface shells`,tooltip:`Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material.`,default:!1},nt={type:`coBool`,mode:`advanced`,label:`Use beam interlocking`,tooltip:`Generate interlocking beam structure at the locations where different filaments touch. This improves the adhesion between filaments, especially models printed in different materials.`,default:!1},rt={type:`coInt`,mode:`advanced`,label:`Interlocking beam layers`,tooltip:`The height of the beams of the interlocking structure, measured in number of layers. Less layers is stronger, but more prone to defects.`,min:1,default:2},it={type:`coFloat`,mode:`advanced`,label:`Interlocking beam width`,tooltip:`The width of the interlocking structure beams.`,sidetext:`mm`,min:.01,default:.8},at={type:`coInt`,mode:`advanced`,label:`Interlocking boundary avoidance`,tooltip:`The distance from the outside of a model where interlocking structures will not be generated, measured in cells.`,min:0,default:2},ot={type:`coInt`,mode:`advanced`,label:`Interlocking depth`,tooltip:`The distance from the boundary between filaments to generate interlocking structure, measured in cells. Too few cells will result in poor adhesion.`,min:1,default:2},st={type:`coFloat`,mode:`advanced`,label:`Interlocking direction`,tooltip:`Orientation of interlock beams.`,sidetext:`°`,min:0,max:360,default:22.5},ct={type:`coFloat`,mode:`advanced`,label:`Internal bridge infill direction`,tooltip:`Internal Bridging angle override.
|
||||
The actual inner wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},Qe={type:`coFloats`,mode:`advanced`,label:`Inner wall`,tooltip:`Jerk of inner walls.`,sidetext:`mm/s`,min:0,default:[9]},$e={type:`coFloatOrPercent`,mode:`advanced`,label:`Inner wall`,tooltip:`Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},et={type:`coFloats`,mode:`advanced`,label:`Inner wall`,tooltip:`This is the speed for inner walls.`,sidetext:`mm/s`,min:1,default:[60]},tt={type:`coBool`,mode:`advanced`,label:`Interface shells`,tooltip:`Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material.`,default:!1},nt={type:`coBool`,mode:`advanced`,label:`Use beam interlocking`,tooltip:`Generate interlocking beam structure at the locations where different filaments touch. This improves the adhesion between filaments, especially models printed in different materials.`,default:!1},rt={type:`coInt`,mode:`advanced`,label:`Interlocking beam layers`,tooltip:`The height of the beams of the interlocking structure, measured in number of layers. Less layers is stronger, but more prone to defects.`,min:1,default:2},it={type:`coFloat`,mode:`advanced`,label:`Interlocking beam width`,tooltip:`The width of the interlocking structure beams.`,sidetext:`mm`,min:.01,default:.8},at={type:`coInt`,mode:`advanced`,label:`Interlocking boundary avoidance`,tooltip:`The distance from the outside of a model where interlocking structures will not be generated, measured in cells.`,min:0,default:2},ot={type:`coInt`,mode:`advanced`,label:`Interlocking depth`,tooltip:`The distance from the boundary between filaments to generate interlocking structure, measured in cells. Too few cells will result in poor adhesion.`,min:1,default:2},st={type:`coFloat`,mode:`advanced`,label:`Interlocking direction`,tooltip:`Orientation of interlock beams.`,sidetext:`°`,min:0,max:360,default:22.5},ct={type:`coFloat`,mode:`advanced`,label:`Internal bridge infill direction`,tooltip:`Internal Bridging angle override.
|
||||
If left to zero, the bridging angle will be calculated automatically for each specific bridge.
|
||||
Otherwise the provided angle will be used according to:
|
||||
- The absolute coordinates
|
||||
|
|
@ -156,10 +156,10 @@ Values below 1.0: Reduce the amount of material while adjusting line spacing to
|
|||
The actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},dt={type:`coInt`,mode:`advanced`,label:`Internal solid infill`,tooltip:`Filament to print internal solid infill.
|
||||
"Default" uses the active object/part filament.`,min:0,default:0},ft={type:`coFloatsOrPercents`,mode:`advanced`,label:`Internal solid infill`,tooltip:`Acceleration of internal solid infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration.`,sidetext:`mm/s² or %`,min:0,default:[`100%`]},pt={type:`coFloat`,mode:`advanced`,label:`Internal solid infill flow ratio`,tooltip:`This factor affects the amount of material for internal solid infill.
|
||||
|
||||
The actual internal solid infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},mt={type:`coFloatOrPercent`,mode:`advanced`,label:`Internal solid infill`,tooltip:`Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},ht={type:`coEnum`,mode:`simple`,label:`Internal solid infill pattern`,tooltip:`Line pattern of internal solid infill. if the detect narrow internal solid infill be enabled, the concentric pattern will be used for the small area.`,enum_values:[`monotonic`,`monotonicline`,`rectilinear`,`alignedrectilinear`,`concentric`,`hilbertcurve`,`archimedeanchords`,`octagramspiral`],enum_labels:[`Monotonic`,`Monotonic line`,`Rectilinear`,`Aligned Rectilinear`,`Concentric`,`Hilbert Curve`,`Archimedean Chords`,`Octagram Spiral`],default:`monotonic`},gt={type:`coFloats`,mode:`advanced`,label:`Internal solid infill`,tooltip:`This is the speed for internal solid infill, not including the top or bottom surface.`,sidetext:`mm/s`,min:1,default:[100]},_t={type:`coFloat`,mode:`advanced`,label:`Ironing angle offset`,tooltip:`The angle of ironing lines offset from the top surface.`,sidetext:`°`,min:0,max:359,default:0},vt={type:`coBool`,mode:`advanced`,label:`Fixed ironing angle`,tooltip:`Use a fixed absolute angle for ironing.`,default:!1},yt={type:`coFloat`,mode:`expert`,label:`Ironing expansion`,tooltip:`Expand or contract the ironing area.`,sidetext:`mm`,min:-100,max:100,default:0},bt={type:`coPercent`,mode:`advanced`,label:`Ironing flow`,tooltip:`This is the amount of material to be extruded during ironing. It is relative to the flow of normal layer height. Too high a value will result in overextrusion on the surface.`,sidetext:`%`,min:0,max:100,default:10},xt={type:`coFloat`,mode:`advanced`,label:`Ironing inset`,tooltip:`The distance to keep from the edges. A value of 0 sets this to half of the nozzle diameter.`,sidetext:`mm`,min:0,max:100,default:0},St={type:`coEnum`,mode:`advanced`,label:`Ironing Pattern`,tooltip:`The pattern that will be used when ironing.`,enum_values:[`rectilinear`,`concentric`],enum_labels:[`Rectilinear`,`Concentric`],default:`rectilinear`},Ct={type:`coFloat`,mode:`advanced`,label:`Ironing line spacing`,tooltip:`This is the distance between the lines used for ironing.`,sidetext:`mm`,min:0,max:1,default:.1},wt={type:`coFloat`,mode:`advanced`,label:`Ironing speed`,tooltip:`This is the print speed for ironing lines.`,sidetext:`mm/s`,min:1,default:20},Tt={type:`coEnum`,mode:`advanced`,label:`Ironing type`,tooltip:`Ironing uses a small flow to print at the same height of a surface to make flat surfaces smoother. This setting controls which layers are being ironed.`,enum_values:[`no ironing`,`top`,`topmost`,`solid`],enum_labels:[`No ironing`,`All top surfaces`,`Topmost surface only`,`All solid layers`],default:`no ironing`},Et={type:`coBool`,mode:`advanced`,label:`Print infill first`,tooltip:`Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.
|
||||
The actual internal solid infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},mt={type:`coFloatOrPercent`,mode:`advanced`,label:`Internal solid infill`,tooltip:`Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},ht={type:`coEnum`,mode:`simple`,label:`Internal solid infill pattern`,tooltip:`Line pattern of internal solid infill. if the detect narrow internal solid infill be enabled, the concentric pattern will be used for the small area.`,enum_values:[`monotonic`,`monotonicline`,`rectilinear`,`alignedrectilinear`,`concentric`,`hilbertcurve`,`archimedeanchords`,`octagramspiral`],enum_labels:[`Monotonic`,`Monotonic line`,`Rectilinear`,`Aligned Rectilinear`,`Concentric`,`Hilbert Curve`,`Archimedean Chords`,`Octagram Spiral`],default:`monotonic`},gt={type:`coFloats`,mode:`advanced`,label:`Internal solid infill`,tooltip:`This is the speed for internal solid infill, not including the top or bottom surface.`,sidetext:`mm/s`,min:1,default:[100]},_t={type:`coFloat`,mode:`advanced`,label:`Ironing angle offset`,tooltip:`The angle of ironing lines offset from the top surface.`,sidetext:`°`,min:0,max:359,default:0},vt={type:`coBool`,mode:`advanced`,label:`Fixed ironing angle`,tooltip:`Use a fixed absolute angle for ironing.`,default:!1},yt={type:`coFloat`,mode:`expert`,label:`Ironing expansion`,tooltip:`Expand or contract the ironing area.`,sidetext:`mm`,min:-100,max:100,default:0},bt={type:`coPercent`,mode:`advanced`,label:`Ironing flow`,tooltip:`This is the amount of material to be extruded during ironing. It is relative to the flow of normal layer height. Too high a value will result in overextrusion on the surface.`,sidetext:`%`,min:0,max:100,default:10},xt={type:`coFloat`,mode:`advanced`,label:`Ironing inset`,tooltip:`The distance to keep from the edges. A value of 0 sets this to half of the nozzle diameter.`,sidetext:`mm`,min:0,max:100,default:0},St={type:`coEnum`,mode:`advanced`,label:`Ironing Pattern`,tooltip:`The pattern that will be used when ironing.`,enum_values:[`rectilinear`,`concentric`],enum_labels:[`Rectilinear`,`Concentric`],default:`rectilinear`},Ct={type:`coFloat`,mode:`advanced`,label:`Ironing line spacing`,tooltip:`This is the distance between the lines used for ironing.`,sidetext:`mm`,min:0,max:1,default:.1},wt={type:`coFloat`,mode:`advanced`,label:`Ironing speed`,tooltip:`This is the print speed for ironing lines.`,sidetext:`mm/s`,min:1,default:20},Tt={type:`coEnum`,mode:`advanced`,label:`Ironing type`,tooltip:`Ironing uses a small flow to print at the same height of a surface to make flat surfaces smoother. This setting controls which layers are being ironed.`,enum_values:[`no ironing`,`top`,`topmost`,`solid`],enum_labels:[`No ironing`,`All top surfaces`,`Topmost surface only`,`All solid layers`],default:`no ironing`},Et={type:`coBool`,mode:`advanced`,label:`Print infill first`,tooltip:`Order of wall/infill. When the tickbox is unchecked the walls are printed first, which works best in most cases.
|
||||
|
||||
Printing infill first may help with extreme overhangs as the walls have the neighbouring infill to adhere to. However, the infill will slightly push out the printed walls where it is attached to them, resulting in a worse external surface finish. It can also cause the infill to shine through the external surfaces of the part.`,default:!1},Dt={type:`coFloat`,mode:`advanced`,label:`Lateral lattice angle 1`,tooltip:`The angle of the first set of Lateral lattice elements in the Z direction. Zero is vertical.`,sidetext:`°`,min:-75,max:75,default:-45},Ot={type:`coFloat`,mode:`advanced`,label:`Lateral lattice angle 2`,tooltip:`The angle of the second set of Lateral lattice elements in the Z direction. Zero is vertical.`,sidetext:`°`,min:-75,max:75,default:45},kt={type:`coFloat`,mode:`simple`,label:`Layer height`,tooltip:`This is the height for each layer. Smaller layer heights give greater accuracy but longer printing time.`,sidetext:`mm`,min:0,default:.2},At={type:`coFloat`,mode:`expert`,label:`Lightning overhang angle`,tooltip:`Maximum overhang angle for Lightning infill support propagation.`,sidetext:`°`,min:5,max:85,default:45},jt={type:`coFloat`,mode:`expert`,label:`Prune angle`,tooltip:`Controls how aggressively short or unsupported Lightning branches are pruned.
|
||||
This angle is converted internally to a per-layer distance.`,sidetext:`°`,min:5,max:85,default:45},Mt={type:`coFloat`,mode:`expert`,label:`Straightening angle`,tooltip:`Maximum straightening angle used to simplify Lightning branches.`,sidetext:`°`,min:5,max:85,default:45},Nt={type:`coFloatOrPercent`,mode:`advanced`,label:`Default`,tooltip:`Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},Pt={type:`coBool`,mode:`advanced`,label:`Make overhangs printable`,tooltip:`Modify the geometry to print overhangs without support material.`,default:!1},Ft={type:`coFloat`,mode:`advanced`,label:`Make overhangs printable - Maximum angle`,tooltip:`Maximum angle of overhangs to allow after making more steep overhangs printable.90° will not change the model at all and allow any overhang, while 0 will replace all overhangs with conical material.`,sidetext:`°`,min:0,max:90,default:55},It={type:`coFloat`,mode:`advanced`,label:`Make overhangs printable - Hole area`,tooltip:`Maximum area of a hole in the base of the model before it's filled by conical material. A value of 0 will fill all the holes in the model base.`,sidetext:`mm²`,min:0,default:0},Lt={type:`coFloat`,mode:`advanced`,label:`Max bridge length`,tooltip:`This is the maximum length of bridges that don\\'t need support. Set it to 0 if you want all bridges to be supported, and set it to a very large value if you don\\'t want any bridges to be supported.`,sidetext:`mm`,min:0,default:10},Rt={type:`coFloatOrPercent`,mode:`advanced`,label:`Avoid crossing walls - Max detour length`,tooltip:`Maximum detour distance for avoiding crossing wall: The printer won\\'t detour if the detour distance is larger than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. A value of 0 will disable this.`,sidetext:`mm or %`,min:0,default:`0.`},zt={type:`coFloat`,mode:`advanced`,label:`Extrusion rate smoothing`,tooltip:`This parameter smooths out sudden extrusion rate changes that happen when the printer transitions from printing a high flow (high speed/larger width) extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.
|
||||
This angle is converted internally to a per-layer distance.`,sidetext:`°`,min:5,max:85,default:45},Mt={type:`coFloat`,mode:`expert`,label:`Straightening angle`,tooltip:`Maximum straightening angle used to simplify Lightning branches.`,sidetext:`°`,min:5,max:85,default:45},Nt={type:`coFloatOrPercent`,mode:`advanced`,label:`Default`,tooltip:`Default line width if other line widths are set to 0. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},Pt={type:`coBool`,mode:`advanced`,label:`Make overhangs printable`,tooltip:`Modify the geometry to print overhangs without support material.`,default:!1},Ft={type:`coFloat`,mode:`advanced`,label:`Make overhangs printable - Maximum angle`,tooltip:`Maximum angle of overhangs to allow after making more steep overhangs printable.90° will not change the model at all and allow any overhang, while 0 will replace all overhangs with conical material.`,sidetext:`°`,min:0,max:90,default:55},It={type:`coFloat`,mode:`advanced`,label:`Make overhangs printable - Hole area`,tooltip:`Maximum area of a hole in the base of the model before it's filled by conical material. A value of 0 will fill all the holes in the model base.`,sidetext:`mm²`,min:0,default:0},Lt={type:`coFloat`,mode:`advanced`,label:`Max bridge length`,tooltip:`This is the maximum length of bridges that don\\'t need support. Set it to 0 if you want all bridges to be supported, and set it to a very large value if you don\\'t want any bridges to be supported.`,sidetext:`mm`,min:0,default:10},Rt={type:`coFloatOrPercent`,mode:`advanced`,label:`Avoid crossing walls - Max detour length`,tooltip:`Maximum detour distance for avoiding crossing wall: The printer won\\'t detour if the detour distance is larger than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. A value of 0 will disable this.`,sidetext:`mm or %`,min:0,default:`0`},zt={type:`coFloat`,mode:`advanced`,label:`Extrusion rate smoothing`,tooltip:`This parameter smooths out sudden extrusion rate changes that happen when the printer transitions from printing a high flow (high speed/larger width) extrusion to a lower flow (lower speed/smaller width) extrusion and vice versa.
|
||||
|
||||
It defines the maximum rate by which the extruded volumetric flow in mm³/s can change over time. Higher values mean higher extrusion rate changes are allowed, resulting in faster speed transitions.
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ Final number of loops is not taking into account while arranging or validating o
|
|||
Warning: If enabled, artifacts can be created if you have some thin features on the next layer, like letters. Set this setting to 0 to remove these artifacts.`,sidetext:`mm or %`,min:0,default:`300%`},Kt={type:`coFloat`,mode:`advanced`,label:`Minimum sparse infill threshold`,tooltip:`Sparse infill areas which are smaller than this threshold value are replaced by internal solid infill.`,sidetext:`mm²`,min:0,default:15},qt={type:`coFloat`,mode:`advanced`,label:`Interlocking depth of a segmented region`,tooltip:`Interlocking depth of a segmented region. It will be ignored if "mmu_segmented_region_max_width" is zero or if "mmu_segmented_region_interlocking_depth" is bigger than "mmu_segmented_region_max_width". Zero disables this feature.`,sidetext:`mm`,min:0,default:0},Jt={type:`coFloat`,mode:`advanced`,label:`Maximum width of a segmented region`,tooltip:`Maximum width of a segmented region. A value of 0 disables this feature.`,sidetext:`mm`,min:0,default:0},Yt={type:`coBool`,mode:`simple`,label:`Only one wall on first layer`,tooltip:`Use only one wall on first layer, to give more space to the bottom infill pattern.`,default:!1},Xt={type:`coBool`,mode:`simple`,label:`Only one wall on top surfaces`,tooltip:`Use only one wall on flat top surfaces, to give more space to the top infill pattern.`,default:!1},Zt={type:`coBool`,mode:`advanced`,label:`Enable`,tooltip:`This option will drop the temperature of the inactive extruders to prevent oozing.`,default:!1},Qt={type:`coFloats`,mode:`advanced`,label:`Outer wall`,tooltip:`Acceleration of outer wall: using a lower value can improve quality.`,sidetext:`mm/s²`,min:0,default:[500]},$t={type:`coInt`,mode:`advanced`,label:`Outer walls`,tooltip:`Filament to print outer walls.
|
||||
"Default" uses the active object/part filament.`,min:0,default:0},en={type:`coFloat`,mode:`advanced`,label:`Outer wall flow ratio`,tooltip:`This factor affects the amount of material for outer walls.
|
||||
|
||||
The actual outer wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},tn={type:`coFloats`,mode:`advanced`,label:`Outer wall`,tooltip:`Jerk of outer walls.`,sidetext:`mm/s`,min:0,default:[9]},nn={type:`coFloatOrPercent`,mode:`advanced`,label:`Outer wall`,tooltip:`Line width of outer wall. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},rn={type:`coFloats`,mode:`advanced`,label:`Outer wall`,tooltip:`This is the printing speed for the outer walls of parts. These are generally printed slower than inner walls for higher quality.`,sidetext:`mm/s`,min:1,default:[60]},an={type:`coFloat`,mode:`advanced`,label:`Overhang flow ratio`,tooltip:`This factor affects the amount of material for overhangs.
|
||||
The actual outer wall flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},tn={type:`coFloats`,mode:`advanced`,label:`Outer wall`,tooltip:`Jerk of outer walls.`,sidetext:`mm/s`,min:0,default:[9]},nn={type:`coFloatOrPercent`,mode:`advanced`,label:`Outer wall`,tooltip:`Line width of outer wall. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},rn={type:`coFloats`,mode:`advanced`,label:`Outer wall`,tooltip:`This is the printing speed for the outer walls of parts. These are generally printed slower than inner walls for higher quality.`,sidetext:`mm/s`,min:1,default:[60]},an={type:`coFloat`,mode:`advanced`,label:`Overhang flow ratio`,tooltip:`This factor affects the amount of material for overhangs.
|
||||
|
||||
The actual overhang flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},on={type:`coBool`,mode:`advanced`,label:`Reverse on even`,tooltip:`Extrude perimeters that have a part over an overhang in the reverse direction on even layers. This alternating pattern can drastically improve steep overhangs.
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ For this setting to be the most effective, it is recommended to set the Reverse
|
|||
Value 0 enables reversal on every even layers regardless.
|
||||
When Detect overhang wall is not enabled, this option is ignored and reversal happens on every even layers regardless.`,sidetext:`mm or %`,min:0,default:`50%`},ln={type:`coString`,mode:`advanced`,label:`Capabilities`,tooltip:`Configuration for the plugin capabilities this preset uses, overriding the global Capabilities configuration. Stored as a raw JSON array and edited through the dialog behind the button, never typed in directly.`,default:``},un={type:`coBool`,mode:`simple`,label:`Precise wall`,tooltip:`Improve shell precision by adjusting outer wall spacing. This also improves layer consistency. NOTE: This option will be ignored for outer-inner or inner-outer-inner wall sequences.`,default:!0},dn={type:`coBool`,mode:`advanced`,label:`Precise Z height`,tooltip:`Enable this to get precise Z height of object after slicing. It will get the precise object height by fine-tuning the layer heights of the last few layers. Note that this is an experimental parameter.`,default:!1},fn={type:`coInt`,mode:`develop`,label:`Preheat steps`,tooltip:`Insert multiple preheat commands (e.g. M104.1). Only useful for Prusa XL. For other printers, please set it to 1.`,min:1,max:10,default:1},pn={type:`coFloat`,mode:`advanced`,label:`Preheat time`,tooltip:`To reduce the waiting time after tool change, Orca can preheat the next tool while the current tool is still in use. This setting specifies the time in seconds to preheat the next tool. Orca will insert a M104 command to preheat the tool in advance.`,sidetext:`s`,min:0,max:120,default:30},mn={type:`coFloat`,mode:`advanced`,label:`Brim width`,tooltip:`Brim width of prime tower, negative number means auto calculated width based on the height of prime tower.`,sidetext:`mm`,min:-1,enum_values:[`-1`],enum_labels:[`Auto`],default:3},hn={type:`coBool`,mode:`advanced`,label:`Internal ribs`,tooltip:`Enable internal ribs to increase the stability of the prime tower.`,default:!1},gn={type:`coPercent`,mode:`advanced`,label:`Infill gap`,tooltip:`Infill gap.`,sidetext:`%`,min:100,default:150},_n={type:`coBool`,mode:`advanced`,label:`Skip points`,tooltip:`The wall of prime tower will skip the start points of wipe path.`,default:!0},vn={type:`coFloat`,mode:`simple`,label:`Width`,tooltip:`This is the width of prime towers.`,sidetext:`mm`,min:2,default:60},yn={type:`coFloat`,mode:`simple`,label:`Prime volume`,tooltip:`This is the volume of material to prime the extruder with on the tower.`,sidetext:`mm³`,min:1,default:45},bn={type:`coFloat`,mode:`advanced`,label:`Flow ratio`,tooltip:`The material may have volumetric change after switching between molten and crystalline states. This setting changes all extrusion flow of this filament in G-code proportionally. The recommended value range is between 0.95 and 1.05. You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow.
|
||||
|
||||
The final object flow ratio is this value multiplied by the filament flow ratio.`,min:`0.01f`,max:`2.f`,default:1},xn={type:`coEnum`,mode:`advanced`,label:`Intra-layer order`,tooltip:`Print order within a single layer.`,enum_values:[`default`,`as_obj_list`],enum_labels:[`Default`,`As object list`],default:`default`},Sn={type:`coEnum`,mode:`simple`,label:`Print sequence`,tooltip:`This determines the print sequence, allowing you to print layer-by-layer or object-by-object.`,enum_values:[`by layer`,`by object`],enum_labels:[`By layer`,`By object`],default:`by layer`},Cn={type:`coFloat`,mode:`advanced`,label:`Raft contact Z distance`,tooltip:`Z gap between raft and object. If Support Top Z Distance is 0, this value is ignored and the object is printed in direct contact with the raft (no gap).`,sidetext:`mm`,min:0,default:.1},wn={type:`coPercent`,mode:`advanced`,label:`First layer density`,tooltip:`This is the density of the first raft or support layer.`,sidetext:`%`,min:10,max:100,default:90},Tn={type:`coFloat`,mode:`advanced`,label:`First layer expansion`,tooltip:`This expands the first raft or support layer to improve bed adhesion.`,sidetext:`mm`,min:0,default:2},En={type:`coInt`,mode:`advanced`,label:`Raft layers`,tooltip:`Object will be raised by this number of support layers. Use this function to avoid warping when printing ABS.`,sidetext:`layers`,min:0,max:100,default:0},Dn={type:`coBool`,mode:`advanced`,label:`Avoid crossing walls`,tooltip:`This detours to avoid traveling across walls, which may cause blobs on the surface.`,default:!1},On={type:`coBool`,mode:`advanced`,label:`Reduce infill retraction`,tooltip:`Don\\'t retract when the travel is entirely within an infill area. That means the oozing can\\'t been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped.`,default:!1},kn={type:`coBool`,mode:`advanced`,label:`Relative bridge angle`,tooltip:`When enabled, the bridge angle values are added to the automatically calculated bridge direction instead of overriding it.`,default:!1},An={type:`coFloat`,mode:`advanced`,label:`Resolution`,tooltip:`The G-code path is generated after simplifying the contour of models to avoid too many points and G-code lines. Smaller values mean higher resolution and more time required to slice.`,sidetext:`mm`,min:0,default:.01},jn={type:`coBool`,mode:`advanced`,label:`Role base wipe speed`,tooltip:`The wipe speed is determined by the speed of the current extrusion role. e.g. if a wipe action is executed immediately following an outer wall extrusion, the speed of the outer wall extrusion will be utilized for the wipe action.`,default:!0},Mn={type:`coInt`,mode:`advanced`,label:`Conditional angle threshold`,tooltip:`This option sets the threshold angle for applying a conditional scarf joint seam.
|
||||
The final object flow ratio is this value multiplied by the filament flow ratio.`,min:`0.01`,max:`2`,default:1},xn={type:`coEnum`,mode:`advanced`,label:`Intra-layer order`,tooltip:`Print order within a single layer.`,enum_values:[`default`,`as_obj_list`],enum_labels:[`Default`,`As object list`],default:`default`},Sn={type:`coEnum`,mode:`simple`,label:`Print sequence`,tooltip:`This determines the print sequence, allowing you to print layer-by-layer or object-by-object.`,enum_values:[`by layer`,`by object`],enum_labels:[`By layer`,`By object`],default:`by layer`},Cn={type:`coFloat`,mode:`advanced`,label:`Raft contact Z distance`,tooltip:`Z gap between raft and object. If Support Top Z Distance is 0, this value is ignored and the object is printed in direct contact with the raft (no gap).`,sidetext:`mm`,min:0,default:.1},wn={type:`coPercent`,mode:`advanced`,label:`First layer density`,tooltip:`This is the density of the first raft or support layer.`,sidetext:`%`,min:10,max:100,default:90},Tn={type:`coFloat`,mode:`advanced`,label:`First layer expansion`,tooltip:`This expands the first raft or support layer to improve bed adhesion.`,sidetext:`mm`,min:0,default:2},En={type:`coInt`,mode:`advanced`,label:`Raft layers`,tooltip:`Object will be raised by this number of support layers. Use this function to avoid warping when printing ABS.`,sidetext:`layers`,min:0,max:100,default:0},Dn={type:`coBool`,mode:`advanced`,label:`Avoid crossing walls`,tooltip:`This detours to avoid traveling across walls, which may cause blobs on the surface.`,default:!1},On={type:`coBool`,mode:`advanced`,label:`Reduce infill retraction`,tooltip:`Don\\'t retract when the travel is entirely within an infill area. That means the oozing can\\'t been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped.`,default:!1},kn={type:`coBool`,mode:`advanced`,label:`Relative bridge angle`,tooltip:`When enabled, the bridge angle values are added to the automatically calculated bridge direction instead of overriding it.`,default:!1},An={type:`coFloat`,mode:`advanced`,label:`Resolution`,tooltip:`The G-code path is generated after simplifying the contour of models to avoid too many points and G-code lines. Smaller values mean higher resolution and more time required to slice.`,sidetext:`mm`,min:0,default:.01},jn={type:`coBool`,mode:`advanced`,label:`Role base wipe speed`,tooltip:`The wipe speed is determined by the speed of the current extrusion role. e.g. if a wipe action is executed immediately following an outer wall extrusion, the speed of the outer wall extrusion will be utilized for the wipe action.`,default:!0},Mn={type:`coInt`,mode:`advanced`,label:`Conditional angle threshold`,tooltip:`This option sets the threshold angle for applying a conditional scarf joint seam.
|
||||
If the maximum angle within the perimeter loop exceeds this value (indicating the absence of sharp corners), a scarf joint seam will be used. The default value is 155°.`,sidetext:`°`,min:0,max:180,default:155},Nn={type:`coFloat`,mode:`expert`,label:`Scarf joint flow ratio`,tooltip:`This factor affects the amount of material for scarf joints.`,min:0,max:2,default:1},Pn={type:`coFloatOrPercent`,mode:`advanced`,label:`Scarf joint speed`,tooltip:`This option sets the printing speed for scarf joints. It is recommended to print scarf joints at a slow speed (less than 100 mm/s). It's also advisable to enable 'Extrusion rate smoothing' if the set speed varies significantly from the speed of the outer or inner walls. If the speed specified here is higher than the speed of the outer or inner walls, the printer will default to the slower of the two speeds. When specified as a percentage (e.g., 80%), the speed is calculated based on the respective outer or inner wall speed. The default value is set to 100%.`,sidetext:`mm/s or %`,min:1,default:`100%`},Fn={type:`coPercent`,mode:`advanced`,label:`Conditional overhang threshold`,tooltip:`This option determines the overhang threshold for the application of scarf joint seams. If the unsupported portion of the perimeter is less than this threshold, scarf joint seams will be applied. The default threshold is set at 40% of the external wall's width. Due to performance considerations, the degree of overhang is estimated.`,sidetext:`%`,min:0,default:40},In={type:`coFloatOrPercent`,mode:`advanced`,label:`Seam gap`,tooltip:`In order to reduce the visibility of the seam in a closed loop extrusion, the loop is interrupted and shortened by a specified amount.
|
||||
This amount can be specified in millimeters or as a percentage of the current extruder diameter. The default value for this parameter is 10%.`,sidetext:`mm or %`,min:0,default:`10%`},Ln={type:`coEnum`,mode:`simple`,label:`Seam position`,tooltip:`This is the starting position for each part of the outer wall.`,enum_values:[`nearest`,`aligned`,`aligned_back`,`back`,`random`],enum_labels:[`Nearest`,`Aligned`,`Aligned back`,`Back`,`Random`],default:`aligned`},Rn={type:`coBool`,mode:`advanced`,label:`Conditional scarf joint`,tooltip:`Apply scarf joints only to smooth perimeters where traditional seams do not conceal the seams at sharp corners effectively.`,default:!1},zn={type:`coBool`,mode:`advanced`,label:`Scarf around entire wall`,tooltip:`The scarf extends to the entire length of the wall.`,default:!1},Bn={type:`coBool`,mode:`advanced`,label:`Scarf joint for inner walls`,tooltip:`Use scarf joint for inner walls as well.`,default:!1},Vn={type:`coFloat`,mode:`advanced`,label:`Scarf length`,tooltip:`Length of the scarf. Setting this parameter to zero effectively disables the scarf.`,sidetext:`mm`,min:0,default:20},Hn={type:`coFloatOrPercent`,mode:`advanced`,label:`Scarf start height`,tooltip:`Start height of the scarf.
|
||||
This amount can be specified in millimeters or as a percentage of the current layer height. The default value for this parameter is 0.`,sidetext:`mm or %`,min:0,default:`0`},Un={type:`coInt`,mode:`advanced`,label:`Scarf steps`,tooltip:`Minimum number of segments of each scarf.`,min:1,default:10},Wn={type:`coEnum`,mode:`advanced`,label:`Scarf joint seam (beta)`,tooltip:`Use scarf joint to minimize seam visibility and increase seam strength.`,enum_values:[`none`,`external`,`all`],enum_labels:[`None`,`Contour`,`Contour and hole`],default:`none`},Gn={type:`coBool`,mode:`expert`,label:`Separated infills`,tooltip:`Centers the internal infill of each part on itself, as if it were sliced on its own, instead of on the whole assembly. Parts that touch or overlap are treated as one body and share a center; separate parts (or distinct 3D objects) each get their own.
|
||||
|
|
@ -218,7 +218,7 @@ Note: When this option is enabled, overhang perimeters are treated like overhang
|
|||
For example, when the perimeters are 100% overhanging, with no wall supporting them from underneath, the 100% overhang speed will be applied.`,default:[!1]},ur={type:`coBool`,mode:`advanced`,label:`Small area flow compensation (beta)`,tooltip:`Enable flow compensation for small infill areas.`,default:!1},dr={type:`coFloatsOrPercents`,mode:`advanced`,label:`Small perimeters`,tooltip:`This separate setting will affect the speed of perimeters having radius <= small_perimeter_threshold (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the outer wall speed setting above. Set to zero for auto.`,sidetext:`mm/s or %`,min:1,default:[`50%`]},fr={type:`coFloats`,mode:`advanced`,label:`Small perimeters threshold`,tooltip:`This sets the threshold for small perimeter length. Default threshold is 0mm.`,sidetext:`mm`,min:0,default:[0]},pr={type:`coFloatsOrPercents`,mode:`advanced`,label:`Small support perimeters`,tooltip:'Same as "Small perimeters", but for supports. This separate setting will affect the speed of support for areas <= `small_support_perimeter_threshold`. If expressed as a percentage (for example: 80%), it will be calculated on the support or support interface speed setting above. Set to zero for auto.',sidetext:`mm/s or %`,min:1,default:[`50%`]},mr={type:`coFloats`,mode:`advanced`,label:`Small support perimeters threshold`,tooltip:`This sets the threshold for small support perimeter length. The default threshold is 0mm.`,sidetext:`mm`,min:0,default:[0]},hr={type:`coFloat`,mode:`advanced`,label:`Solid infill direction`,tooltip:`Angle for solid infill pattern, which controls the start or main direction of line.`,sidetext:`°`,min:0,max:360,default:45},gr={type:`coString`,mode:`advanced`,label:`Solid infill rotation template`,tooltip:`This parameter adds a rotation of solid infill direction to each layer according to the specified template. The template is a comma-separated list of angles in degrees, e.g. '0,90'. The first angle is applied to the first layer, the second angle to the second layer, and so on. If there are more layers than angles, the angles will be repeated. Note that not all solid infill patterns support rotation.`,sidetext:`°`,default:``},_r={type:`coFloatsOrPercents`,mode:`advanced`,label:`Sparse infill`,tooltip:`Acceleration of sparse infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration.`,sidetext:`mm/s² or %`,min:0,default:[`100%`]},vr={type:`coPercent`,mode:`simple`,label:`Sparse infill density`,tooltip:`Density of internal sparse infill, 100% turns all sparse infill into solid infill and internal solid infill pattern will be used.`,sidetext:`%`,min:0,max:100,default:20},yr={type:`coInt`,mode:`advanced`,label:`Infill`,tooltip:`Filament to print internal sparse infill.
|
||||
"Default" uses the active object/part filament.`,min:0,default:0},br={type:`coFloat`,mode:`advanced`,label:`Sparse infill flow ratio`,tooltip:`This factor affects the amount of material for sparse infill.
|
||||
|
||||
The actual sparse infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},xr={type:`coFloatOrPercent`,mode:`advanced`,label:`Sparse infill`,tooltip:`Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},Sr={type:`coEnum`,mode:`simple`,label:`Sparse infill pattern`,tooltip:`This is the line pattern for internal sparse infill.`,enum_values:`rectilinear.alignedrectilinear.zigzag.crosszag.lockedzag.line.grid.triangles.tri-hexagon.cubic.adaptivecubic.quartercubic.supportcubic.lightning.honeycomb.3dhoneycomb.lateral-honeycomb.lateral-lattice.crosshatch.tpmsd.tpmsfk.gyroid.concentric.hilbertcurve.archimedeanchords.octagramspiral`.split(`.`),enum_labels:`Rectilinear.Aligned Rectilinear.Zig Zag.Cross Zag.Locked Zag.Line.Grid.Triangles.Tri-hexagon.Cubic.Adaptive Cubic.Quarter Cubic.Support Cubic.Lightning.Honeycomb.3D Honeycomb.Lateral Honeycomb.Lateral Lattice.Cross Hatch.TPMS-D.TPMS-FK.Gyroid.Concentric.Hilbert Curve.Archimedean Chords.Octagram Spiral`.split(`.`),default:`crosshatch`},Cr={type:`coString`,mode:`advanced`,label:`Sparse infill rotation template`,tooltip:`Rotate the sparse infill direction per layer using a template of angles. Enter comma-separated degrees (e.g., '0,30,60,90'). Angles are applied in order by layer and repeat when the list ends. Advanced syntax is supported: '+5' rotates +5° every layer; '+5#5' rotates +5° every 5 layers. See the Wiki for details. When a template is set, the standard infill direction setting is ignored. Note: some infill patterns (e.g., Gyroid) control rotation themselves; use with care.`,sidetext:`°`,default:``},wr={type:`coFloats`,mode:`advanced`,label:`Sparse infill`,tooltip:`This is the speed for internal sparse infill.`,sidetext:`mm/s`,min:1,default:[100]},Tr={type:`coFloat`,mode:`advanced`,label:`Spiral finishing flow ratio`,tooltip:`Sets the finishing flow ratio while ending the spiral. Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop which can in some cases lead to under extrusion at the end of the spiral.`,min:0,max:1,default:0},Er={type:`coBool`,mode:`simple`,label:`Spiral vase`,tooltip:`This enables spiraling, which smooths out the Z moves of the outer contour and turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam.`,default:!1},Dr={type:`coFloatOrPercent`,mode:`advanced`,label:`Max XY Smoothing`,tooltip:`Maximum distance to move points in XY to try to achieve a smooth spiral. If expressed as a %, it will be computed over nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`200%`},Or={type:`coBool`,mode:`simple`,label:`Smooth Spiral`,tooltip:`Smooth Spiral smooths out X and Y moves as well, resulting in no visible seam at all, even in the XY directions on walls that are not vertical.`,default:!1},kr={type:`coFloat`,mode:`advanced`,label:`Spiral starting flow ratio`,tooltip:`Sets the starting flow ratio while transitioning from the last bottom layer to the spiral. Normally the spiral transition scales the flow ratio from 0% to 100% during the first loop which can in some cases lead to under extrusion at the start of the spiral.`,min:0,max:1,default:0},Ar={type:`coBool`,mode:`advanced`,label:`Staggered inner seams`,tooltip:`This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern.`,default:!1},jr={type:`coInt`,mode:`advanced`,label:`Temperature variation`,tooltip:`Temperature difference to be applied when an extruder is not active. The value is not used when 'idle_temperature' in filament settings is set to non-zero value.`,sidetext:`\\u2206\\u2103`,min:`-max_temp`,max:`max_temp`,default:-5},Mr={type:`coFloat`,mode:`advanced`,label:`Pattern angle`,tooltip:`Use this setting to rotate the support pattern on the horizontal plane.`,sidetext:`°`,min:0,max:359,default:0},Nr={type:`coEnum`,mode:`advanced`,label:`Base pattern`,tooltip:`Line pattern of support.
|
||||
The actual sparse infill flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},xr={type:`coFloatOrPercent`,mode:`advanced`,label:`Sparse infill`,tooltip:`Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},Sr={type:`coEnum`,mode:`simple`,label:`Sparse infill pattern`,tooltip:`This is the line pattern for internal sparse infill.`,enum_values:`rectilinear.alignedrectilinear.zigzag.crosszag.lockedzag.line.grid.triangles.tri-hexagon.cubic.adaptivecubic.quartercubic.supportcubic.lightning.honeycomb.3dhoneycomb.lateral-honeycomb.lateral-lattice.crosshatch.tpmsd.tpmsfk.gyroid.concentric.hilbertcurve.archimedeanchords.octagramspiral`.split(`.`),enum_labels:`Rectilinear.Aligned Rectilinear.Zig Zag.Cross Zag.Locked Zag.Line.Grid.Triangles.Tri-hexagon.Cubic.Adaptive Cubic.Quarter Cubic.Support Cubic.Lightning.Honeycomb.3D Honeycomb.Lateral Honeycomb.Lateral Lattice.Cross Hatch.TPMS-D.TPMS-FK.Gyroid.Concentric.Hilbert Curve.Archimedean Chords.Octagram Spiral`.split(`.`),default:`crosshatch`},Cr={type:`coString`,mode:`advanced`,label:`Sparse infill rotation template`,tooltip:`Rotate the sparse infill direction per layer using a template of angles. Enter comma-separated degrees (e.g., '0,30,60,90'). Angles are applied in order by layer and repeat when the list ends. Advanced syntax is supported: '+5' rotates +5° every layer; '+5#5' rotates +5° every 5 layers. See the Wiki for details. When a template is set, the standard infill direction setting is ignored. Note: some infill patterns (e.g., Gyroid) control rotation themselves; use with care.`,sidetext:`°`,default:``},wr={type:`coFloats`,mode:`advanced`,label:`Sparse infill`,tooltip:`This is the speed for internal sparse infill.`,sidetext:`mm/s`,min:1,default:[100]},Tr={type:`coFloat`,mode:`advanced`,label:`Spiral finishing flow ratio`,tooltip:`Sets the finishing flow ratio while ending the spiral. Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop which can in some cases lead to under extrusion at the end of the spiral.`,min:0,max:1,default:0},Er={type:`coBool`,mode:`simple`,label:`Spiral vase`,tooltip:`This enables spiraling, which smooths out the Z moves of the outer contour and turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam.`,default:!1},Dr={type:`coFloatOrPercent`,mode:`advanced`,label:`Max XY Smoothing`,tooltip:`Maximum distance to move points in XY to try to achieve a smooth spiral. If expressed as a %, it will be computed over nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`200%`},Or={type:`coBool`,mode:`simple`,label:`Smooth Spiral`,tooltip:`Smooth Spiral smooths out X and Y moves as well, resulting in no visible seam at all, even in the XY directions on walls that are not vertical.`,default:!1},kr={type:`coFloat`,mode:`advanced`,label:`Spiral starting flow ratio`,tooltip:`Sets the starting flow ratio while transitioning from the last bottom layer to the spiral. Normally the spiral transition scales the flow ratio from 0% to 100% during the first loop which can in some cases lead to under extrusion at the start of the spiral.`,min:0,max:1,default:0},Ar={type:`coBool`,mode:`advanced`,label:`Staggered inner seams`,tooltip:`This option causes the inner seams to be shifted backwards based on their depth, forming a zigzag pattern.`,default:!1},jr={type:`coInt`,mode:`advanced`,label:`Temperature variation`,tooltip:`Temperature difference to be applied when an extruder is not active. The value is not used when 'idle_temperature' in filament settings is set to non-zero value.`,sidetext:`\\u2206\\u2103`,min:`-max_temp`,max:`max_temp`,default:-5},Mr={type:`coFloat`,mode:`advanced`,label:`Pattern angle`,tooltip:`Use this setting to rotate the support pattern on the horizontal plane.`,sidetext:`°`,min:0,max:359,default:0},Nr={type:`coEnum`,mode:`advanced`,label:`Base pattern`,tooltip:`Line pattern of support.
|
||||
|
||||
The Default option for Tree supports is Hollow, which means no base pattern. For other support types, the Default option is the Rectilinear pattern.
|
||||
|
||||
|
|
@ -229,9 +229,9 @@ The actual support flow used is calculated by multiplying this value by the fila
|
|||
"Default" means no specific filament for support interface and current filament is used.`,min:0,default:0},Ur={type:`coFloat`,mode:`advanced`,label:`Support interface flow ratio`,tooltip:`This factor affects the amount of material for the support interface.
|
||||
|
||||
The actual support interface flow used is calculated by multiplying this value by the filament flow ratio, and if set, the object's flow ratio.`,min:0,max:2,default:1},Wr={type:`coBool`,mode:`advanced`,label:`Loop pattern interface`,tooltip:`This covers the top contact layer of the supports with loops. It is disabled by default.`,default:!1},Gr={type:`coBool`,mode:`simple`,label:`Avoid interface filament for base`,tooltip:`Avoid using support interface filament to print support base if possible.`,default:!0},Kr={type:`coEnum`,mode:`advanced`,label:`Interface pattern`,tooltip:`This is the line pattern for support interfaces. The default pattern for non-soluble support interfaces is Rectilinear while the default pattern for soluble support interfaces is Concentric.`,enum_values:[`auto`,`rectilinear`,`concentric`,`rectilinear_interlaced`,`grid`],enum_labels:[`Default`,`Rectilinear`,`Concentric`,`Rectilinear Interlaced`,`Grid`],default:`auto`},qr={type:`coFloat`,mode:`advanced`,label:`Top interface spacing`,tooltip:`Spacing of interface lines. Zero means solid interface.
|
||||
Force using solid interface when support ironing is enabled.`,sidetext:`mm`,min:0,default:.5},Jr={type:`coFloats`,mode:`advanced`,label:`Support interface`,tooltip:`This is the speed for support interfaces.`,sidetext:`mm/s`,min:1,default:[80]},Yr={type:`coInt`,mode:`advanced`,label:`Top interface layers`,tooltip:`This is the number of top interface layers.`,sidetext:`layers`,min:0,enum_values:[`0`,`1`,`2`,`3`],enum_labels:[`0`,`1`,`2`,`3`],default:3},Xr={type:`coBool`,mode:`advanced`,label:`Ironing Support Interface`,tooltip:`Ironing is using small flow to print on same height of support interface again to make it more smooth. This setting controls whether support interface being ironed. When enabled, support interface will be extruded as solid too.`,default:!1},Zr={type:`coPercent`,mode:`advanced`,label:`Support Ironing flow`,tooltip:`The amount of material to extrude during ironing. Relative to flow of normal support interface layer height. Too high value results in overextrusion on the surface.`,sidetext:`%`,min:0,max:100,default:10},Qr={type:`coEnum`,mode:`advanced`,label:`Support Ironing Pattern`,tooltip:`The pattern that will be used when ironing.`,enum_values:[`rectilinear`,`concentric`],enum_labels:[`Rectilinear`,`Concentric`],default:`rectilinear`},$r={type:`coFloat`,mode:`advanced`,label:`Support Ironing line spacing`,tooltip:`This is the distance between the lines used for ironing.`,sidetext:`mm`,min:0,max:1,default:.1},ei={type:`coFloatOrPercent`,mode:`advanced`,label:`Support`,tooltip:`Line width of support. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},ti={type:`coFloat`,mode:`advanced`,label:`Support/object first layer gap`,tooltip:`XY separation between an object and its support at the first layer.`,sidetext:`mm`,min:0,max:10,default:.2},ni={type:`coFloat`,mode:`advanced`,label:`Support/object XY distance`,tooltip:`This controls the XY separation between an object and its support.`,sidetext:`mm`,min:0,max:10,default:.35},ri={type:`coBool`,mode:`simple`,label:`On build plate only`,tooltip:`This setting only generates supports that begin on the build plate.`,default:!1},ii={type:`coBool`,mode:`advanced`,label:`Ignore small overhangs`,tooltip:`Ignore small overhangs that possibly don't require support.`,default:!0},ai={type:`coFloats`,mode:`advanced`,label:`Support`,tooltip:`This is the speed for support.`,sidetext:`mm/s`,min:1,default:[80]},oi={type:`coEnum`,mode:`advanced`,label:`Style`,tooltip:`Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.
|
||||
Force using solid interface when support ironing is enabled.`,sidetext:`mm`,min:0,default:.5},Jr={type:`coFloats`,mode:`advanced`,label:`Support interface`,tooltip:`This is the speed for support interfaces.`,sidetext:`mm/s`,min:1,default:[80]},Yr={type:`coInt`,mode:`advanced`,label:`Top interface layers`,tooltip:`This is the number of top interface layers.`,sidetext:`layers`,min:0,enum_values:[`0`,`1`,`2`,`3`],enum_labels:[`0`,`1`,`2`,`3`],default:3},Xr={type:`coBool`,mode:`advanced`,label:`Ironing Support Interface`,tooltip:`Ironing is using small flow to print on same height of support interface again to make it more smooth. This setting controls whether support interface being ironed. When enabled, support interface will be extruded as solid too.`,default:!1},Zr={type:`coPercent`,mode:`advanced`,label:`Support Ironing flow`,tooltip:`The amount of material to extrude during ironing. Relative to flow of normal support interface layer height. Too high value results in overextrusion on the surface.`,sidetext:`%`,min:0,max:100,default:10},Qr={type:`coEnum`,mode:`advanced`,label:`Support Ironing Pattern`,tooltip:`The pattern that will be used when ironing.`,enum_values:[`rectilinear`,`concentric`],enum_labels:[`Rectilinear`,`Concentric`],default:`rectilinear`},$r={type:`coFloat`,mode:`advanced`,label:`Support Ironing line spacing`,tooltip:`This is the distance between the lines used for ironing.`,sidetext:`mm`,min:0,max:1,default:.1},ei={type:`coFloatOrPercent`,mode:`advanced`,label:`Support`,tooltip:`Line width of support. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},ti={type:`coFloat`,mode:`advanced`,label:`Support/object first layer gap`,tooltip:`XY separation between an object and its support at the first layer.`,sidetext:`mm`,min:0,max:10,default:.2},ni={type:`coFloat`,mode:`advanced`,label:`Support/object XY distance`,tooltip:`This controls the XY separation between an object and its support.`,sidetext:`mm`,min:0,max:10,default:.35},ri={type:`coBool`,mode:`simple`,label:`On build plate only`,tooltip:`This setting only generates supports that begin on the build plate.`,default:!1},ii={type:`coBool`,mode:`advanced`,label:`Ignore small overhangs`,tooltip:`Ignore small overhangs that possibly don't require support.`,default:!0},ai={type:`coFloats`,mode:`advanced`,label:`Support`,tooltip:`This is the speed for support.`,sidetext:`mm/s`,min:1,default:[80]},oi={type:`coEnum`,mode:`advanced`,label:`Style`,tooltip:`Style and shape of the support. For normal support, projecting the supports into a regular grid will create more stable supports (default), while snug support towers will save material and reduce object scarring.
|
||||
For tree support, slim and organic style will merge branches more aggressively and save a lot of material (default organic), while hybrid style will create similar structure to normal support under large flat overhangs.`,enum_values:[`default`,`grid`,`snug`,`organic`,`tree_slim`,`tree_strong`,`tree_hybrid`],enum_labels:[`Default (Grid/Organic)`,`Grid`,`Snug`,`Organic`,`Tree Slim`,`Tree Strong`,`Tree Hybrid`],default:`default`},si={type:`coInt`,mode:`simple`,label:`Threshold angle`,tooltip:`Support will be generated for overhangs whose slope angle is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support.
|
||||
Note: If set to 0, normal supports use the Threshold overlap instead, while tree supports fall back to a default value of 30.`,sidetext:`°`,min:0,max:90,default:30},ci={type:`coFloatOrPercent`,mode:`simple`,label:`Threshold overlap`,tooltip:`If threshold angle is zero, support will be generated for overhangs whose overlap is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support.`,sidetext:`mm or %`,min:0,max:100,default:`50.%`},li={type:`coFloat`,mode:`advanced`,label:`Top Z distance`,tooltip:`Z gap between the support\\'s top and object.`,sidetext:`mm`,min:0,default:.2},ui={type:`coEnum`,mode:`simple`,label:`Type`,tooltip:`Normal (auto) and Tree (auto) are used to generate support automatically. If Normal (manual) or Tree (manual) is selected, only support enforcers are generated.`,enum_values:[`normal(auto)`,`tree(auto)`,`normal(manual)`,`tree(manual)`],enum_labels:[`Normal (auto)`,`Tree (auto)`,`Normal (manual)`,`Tree (manual)`],default:`normal(auto)`},di={type:`coBool`,mode:`advanced`,label:`Symmetric infill Y axis`,tooltip:`If the model has two parts that are symmetric about the Y axis, and you want these parts to have symmetric textures, please click this option on one of the parts.`,default:!1},fi={type:`coBool`,mode:`advanced`,label:`Thick external bridges`,tooltip:`If enabled, bridge extrusion uses a line height equal to the nozzle diameter.
|
||||
Note: If set to 0, normal supports use the Threshold overlap instead, while tree supports fall back to a default value of 30.`,sidetext:`°`,min:0,max:90,default:30},ci={type:`coFloatOrPercent`,mode:`simple`,label:`Threshold overlap`,tooltip:`If threshold angle is zero, support will be generated for overhangs whose overlap is below the threshold. The smaller this value is, the steeper the overhang that can be printed without support.`,sidetext:`mm or %`,min:0,max:100,default:`50%`},li={type:`coFloat`,mode:`advanced`,label:`Top Z distance`,tooltip:`Z gap between the support\\'s top and object.`,sidetext:`mm`,min:0,default:.2},ui={type:`coEnum`,mode:`simple`,label:`Type`,tooltip:`Normal (auto) and Tree (auto) are used to generate support automatically. If Normal (manual) or Tree (manual) is selected, only support enforcers are generated.`,enum_values:[`normal(auto)`,`tree(auto)`,`normal(manual)`,`tree(manual)`],enum_labels:[`Normal (auto)`,`Tree (auto)`,`Normal (manual)`,`Tree (manual)`],default:`normal(auto)`},di={type:`coBool`,mode:`advanced`,label:`Symmetric infill Y axis`,tooltip:`If the model has two parts that are symmetric about the Y axis, and you want these parts to have symmetric textures, please click this option on one of the parts.`,default:!1},fi={type:`coBool`,mode:`advanced`,label:`Thick external bridges`,tooltip:`If enabled, bridge extrusion uses a line height equal to the nozzle diameter.
|
||||
This increases bridge strength and reliability, allowing longer spans, but may worsen appearance.
|
||||
If disabled, bridges may look better but are generally reliable only for shorter spans.`,default:!1},pi={type:`coBool`,mode:`advanced`,label:`Thick internal bridges`,tooltip:`If enabled, internal bridge extrusion uses a line height equal to the nozzle diameter.
|
||||
This increases internal bridge strength and reliability when printed over sparse infill, but may worsen appearance.
|
||||
|
|
@ -249,10 +249,10 @@ This can cause contraction marks (such as the hull line) on the outer walls.
|
|||
By adding a small margin, this contraction will not occur directly on the walls, thereby preventing a visible mark.`,sidetext:`mm`,min:0,max:10,default:0},Ei={type:`coInt`,mode:`advanced`,label:`Top surface`,tooltip:`Filament to print top surface.
|
||||
"Default" uses the active object/part filament.`,min:0,default:0},Di={type:`coEnum`,mode:`advanced`,label:`Top surface fill order`,tooltip:`Direction in which top surfaces are filled when using a center-based pattern (Concentric, Archimedean Chords, Octagram Spiral).
|
||||
Outward starts at the center of the surface, so any excess material is pushed towards the edge where it is least visible. Inward starts at the edge and ends with the tight curves at the center.
|
||||
Default uses shortest-path ordering, which may run in either direction.`,enum_values:[`default`,`outward`,`inward`],enum_labels:[`Default`,`Outward`,`Inward`],default:`default`},Oi={type:`coFloats`,mode:`advanced`,label:`Top surface`,tooltip:`Jerk for top surface.`,sidetext:`mm/s`,min:0,default:[9]},ki={type:`coFloatOrPercent`,mode:`advanced`,label:`Top surface`,tooltip:`Line width for top surfaces. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0.`},Ai={type:`coEnum`,mode:`simple`,label:`Top surface pattern`,tooltip:`This is the line pattern for top surface infill.`,enum_values:[`monotonic`,`monotonicline`,`rectilinear`,`alignedrectilinear`,`concentric`,`hilbertcurve`,`archimedeanchords`,`octagramspiral`],enum_labels:[`Monotonic`,`Monotonic line`,`Rectilinear`,`Aligned Rectilinear`,`Concentric`,`Hilbert Curve`,`Archimedean Chords`,`Octagram Spiral`],default:`monotonicline`},ji={type:`coFloats`,mode:`advanced`,label:`Top surface`,tooltip:`This is the speed for solid top surface infill.`,sidetext:`mm/s`,min:1,default:[100]},Mi={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`Acceleration of travel moves.`,sidetext:`mm/s²`,min:0,default:[1e4]},Ni={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`Jerk for travel.`,sidetext:`mm/s`,min:0,default:[12]},Pi={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`This is the speed at which traveling is done.`,sidetext:`mm/s`,min:1,default:[120]},Fi={type:`coFloat`,mode:`advanced`,label:`Preferred Branch Angle`,tooltip:`The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.`,sidetext:`°`,min:10,max:85,default:25},Ii={type:`coBool`,mode:`simple`,label:`Auto brim width`,tooltip:`Enabling this option means the width of the brim for tree support will be automatically calculated.`,default:!0},Li={type:`coFloat`,mode:`advanced`,label:`Tree support branch angle`,tooltip:`This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther.`,sidetext:`°`,min:0,max:60,default:40},Ri={type:`coFloat`,mode:`advanced`,label:`Tree support branch angle`,tooltip:`This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther.`,sidetext:`°`,min:0,max:60,default:40},zi={type:`coFloat`,mode:`advanced`,label:`Tree support branch diameter`,tooltip:`This setting determines the initial diameter of support nodes.`,sidetext:`mm`,min:1,max:10,default:5},Bi={type:`coFloat`,mode:`advanced`,label:`Branch Diameter Angle`,tooltip:`The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the organic support.`,sidetext:`°`,min:0,max:15,default:5},Vi={type:`coFloat`,mode:`advanced`,label:`Tree support branch diameter`,tooltip:`This setting determines the initial diameter of support nodes.`,sidetext:`mm`,min:1,max:10,default:2},Hi={type:`coFloat`,mode:`advanced`,label:`Tree support branch distance`,tooltip:`This setting determines the distance between neighboring tree support nodes.`,sidetext:`mm`,min:1,max:10,default:5},Ui={type:`coFloat`,mode:`advanced`,label:`Tree support branch distance`,tooltip:`This setting determines the distance between neighboring tree support nodes.`,sidetext:`mm`,min:1,max:10,default:1},Wi={type:`coFloat`,mode:`simple`,label:`Tree support brim width`,tooltip:`Distance from tree branch to the outermost brim line.`,min:0,default:3},Gi={type:`coFloat`,mode:`advanced`,label:`Tip Diameter`,tooltip:`Branch tip diameter for organic supports.`,sidetext:`mm`,min:`0.1f`,max:`100.f`,default:.8},Ki={type:`coPercent`,mode:`advanced`,label:`Branch Density`,tooltip:`Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed.`,sidetext:`%`,min:5,default:30},qi={type:`coInt`,mode:`advanced`,label:`Support wall loops`,tooltip:`This setting specifies the count of support walls in the range of [0,2]. 0 means auto.`,min:0,max:2,default:0},Ji={type:`coEnum`,mode:`advanced`,label:`Wall loop direction`,tooltip:`The direction which the contour wall loops are extruded when looking down from the top.
|
||||
Default uses shortest-path ordering, which may run in either direction.`,enum_values:[`default`,`outward`,`inward`],enum_labels:[`Default`,`Outward`,`Inward`],default:`default`},Oi={type:`coFloats`,mode:`advanced`,label:`Top surface`,tooltip:`Jerk for top surface.`,sidetext:`mm/s`,min:0,default:[9]},ki={type:`coFloatOrPercent`,mode:`advanced`,label:`Top surface`,tooltip:`Line width for top surfaces. If expressed as a %, it will be computed over the nozzle diameter.`,sidetext:`mm or %`,min:0,max:1e3,default:`0`},Ai={type:`coEnum`,mode:`simple`,label:`Top surface pattern`,tooltip:`This is the line pattern for top surface infill.`,enum_values:[`monotonic`,`monotonicline`,`rectilinear`,`alignedrectilinear`,`concentric`,`hilbertcurve`,`archimedeanchords`,`octagramspiral`],enum_labels:[`Monotonic`,`Monotonic line`,`Rectilinear`,`Aligned Rectilinear`,`Concentric`,`Hilbert Curve`,`Archimedean Chords`,`Octagram Spiral`],default:`monotonicline`},ji={type:`coFloats`,mode:`advanced`,label:`Top surface`,tooltip:`This is the speed for solid top surface infill.`,sidetext:`mm/s`,min:1,default:[100]},Mi={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`Acceleration of travel moves.`,sidetext:`mm/s²`,min:0,default:[1e4]},Ni={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`Jerk for travel.`,sidetext:`mm/s`,min:0,default:[12]},Pi={type:`coFloats`,mode:`advanced`,label:`Travel`,tooltip:`This is the speed at which traveling is done.`,sidetext:`mm/s`,min:1,default:[120]},Fi={type:`coFloat`,mode:`advanced`,label:`Preferred Branch Angle`,tooltip:`The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.`,sidetext:`°`,min:10,max:85,default:25},Ii={type:`coBool`,mode:`simple`,label:`Auto brim width`,tooltip:`Enabling this option means the width of the brim for tree support will be automatically calculated.`,default:!0},Li={type:`coFloat`,mode:`advanced`,label:`Tree support branch angle`,tooltip:`This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther.`,sidetext:`°`,min:0,max:60,default:40},Ri={type:`coFloat`,mode:`advanced`,label:`Tree support branch angle`,tooltip:`This setting determines the maximum overhang angle that the branches of tree support are allowed to make. If the angle is increased, the branches can be printed more horizontally, allowing them to reach farther.`,sidetext:`°`,min:0,max:60,default:40},zi={type:`coFloat`,mode:`advanced`,label:`Tree support branch diameter`,tooltip:`This setting determines the initial diameter of support nodes.`,sidetext:`mm`,min:1,max:10,default:5},Bi={type:`coFloat`,mode:`advanced`,label:`Branch Diameter Angle`,tooltip:`The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length. A bit of an angle can increase stability of the organic support.`,sidetext:`°`,min:0,max:15,default:5},Vi={type:`coFloat`,mode:`advanced`,label:`Tree support branch diameter`,tooltip:`This setting determines the initial diameter of support nodes.`,sidetext:`mm`,min:1,max:10,default:2},Hi={type:`coFloat`,mode:`advanced`,label:`Tree support branch distance`,tooltip:`This setting determines the distance between neighboring tree support nodes.`,sidetext:`mm`,min:1,max:10,default:5},Ui={type:`coFloat`,mode:`advanced`,label:`Tree support branch distance`,tooltip:`This setting determines the distance between neighboring tree support nodes.`,sidetext:`mm`,min:1,max:10,default:1},Wi={type:`coFloat`,mode:`simple`,label:`Tree support brim width`,tooltip:`Distance from tree branch to the outermost brim line.`,min:0,default:3},Gi={type:`coFloat`,mode:`advanced`,label:`Tip Diameter`,tooltip:`Branch tip diameter for organic supports.`,sidetext:`mm`,min:`0.1`,max:`100`,default:.8},Ki={type:`coPercent`,mode:`advanced`,label:`Branch Density`,tooltip:`Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces instead of a high branch density value if dense interfaces are needed.`,sidetext:`%`,min:5,default:30},qi={type:`coInt`,mode:`advanced`,label:`Support wall loops`,tooltip:`This setting specifies the count of support walls in the range of [0,2]. 0 means auto.`,min:0,max:2,default:0},Ji={type:`coEnum`,mode:`advanced`,label:`Wall loop direction`,tooltip:`The direction which the contour wall loops are extruded when looking down from the top.
|
||||
Holes are printed in the opposite direction to the contour to maintain alignment with layers whose contour polygons are incomplete and change direction, also partially forming the contour of a hole.
|
||||
|
||||
This option will be disabled if spiral vase mode is enabled.`,enum_values:[`ccw`,`cw`],enum_labels:[`Counter clockwise`,`Clockwise`],default:`ccw`},Yi={type:`coInt`,mode:`advanced`,label:`Wall distribution count`,tooltip:`The number of walls, counted from the center, over which the variation needs to be spread. Lower values mean that the outer walls don't change in width.`,min:1,default:1},Xi={type:`coEnum`,mode:`advanced`,label:`Wall generator`,tooltip:`The classic wall generator produces walls with constant extrusion width and for very thin areas, gap-fill is used. The Arachne engine produces walls with variable extrusion width.`,enum_values:[`classic`,`arachne`],enum_labels:[`Classic`,`Arachne`],default:`arachne`},Zi={type:`coInt`,mode:`simple`,label:`Wall loops`,tooltip:`This is the number of walls per layer.`,min:0,max:1e3,default:2},Qi={type:`coFloat`,mode:`expert`,label:`Maximum wall deviation`,tooltip:`The maximum deviation allowed when reducing the resolution for the 'Maximum wall resolution' setting. If you increase this, the print will be less accurate, but the G-Code will be smaller. 'Maximum wall deviation' limits 'Maximum wall resolution', so if the two conflict, 'Maximum wall deviation' takes precedence.`,sidetext:`mm`,min:`0.005f`,max:`0.05f`,default:.025},$i={type:`coFloat`,mode:`expert`,label:`Maximum wall resolution`,tooltip:`This value determines the smallest wall line segment length in mm. The smaller you set this value, the more accurate and precise the walls will be.`,sidetext:`mm`,min:.005,max:`0.5f`,default:.5},ea={type:`coEnum`,mode:`advanced`,label:`Walls printing order`,tooltip:`Print sequence of the internal (inner) and external (outer) walls.
|
||||
This option will be disabled if spiral vase mode is enabled.`,enum_values:[`ccw`,`cw`],enum_labels:[`Counter clockwise`,`Clockwise`],default:`ccw`},Yi={type:`coInt`,mode:`advanced`,label:`Wall distribution count`,tooltip:`The number of walls, counted from the center, over which the variation needs to be spread. Lower values mean that the outer walls don't change in width.`,min:1,default:1},Xi={type:`coEnum`,mode:`advanced`,label:`Wall generator`,tooltip:`The classic wall generator produces walls with constant extrusion width and for very thin areas, gap-fill is used. The Arachne engine produces walls with variable extrusion width.`,enum_values:[`classic`,`arachne`],enum_labels:[`Classic`,`Arachne`],default:`arachne`},Zi={type:`coInt`,mode:`simple`,label:`Wall loops`,tooltip:`This is the number of walls per layer.`,min:0,max:1e3,default:2},Qi={type:`coFloat`,mode:`expert`,label:`Maximum wall deviation`,tooltip:`The maximum deviation allowed when reducing the resolution for the 'Maximum wall resolution' setting. If you increase this, the print will be less accurate, but the G-Code will be smaller. 'Maximum wall deviation' limits 'Maximum wall resolution', so if the two conflict, 'Maximum wall deviation' takes precedence.`,sidetext:`mm`,min:`0.005`,max:`0.05`,default:.025},$i={type:`coFloat`,mode:`expert`,label:`Maximum wall resolution`,tooltip:`This value determines the smallest wall line segment length in mm. The smaller you set this value, the more accurate and precise the walls will be.`,sidetext:`mm`,min:.005,max:`0.5`,default:.5},ea={type:`coEnum`,mode:`advanced`,label:`Walls printing order`,tooltip:`Print sequence of the internal (inner) and external (outer) walls.
|
||||
|
||||
Use Inner/Outer for best overhangs. This is because the overhanging walls can adhere to a neighbouring perimeter while printing. However, this option results in slightly reduced surface quality as the external perimeter is deformed by being squashed to the internal perimeter.
|
||||
|
||||
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-D5dXOgnd.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DcBH50JZ.css">
|
||||
<script type="module" crossorigin src="/assets/index-DGpcnZPX.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D4VkH83v.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue