mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-22 11:16:06 +03:00
Compare commits
5 Commits
feature/te
...
fix/pull-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09b96dfe99 | ||
|
|
100a9a20d1 | ||
|
|
97c7afa2af | ||
|
|
02c9ab6a02 | ||
|
|
d84ac149d1 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Custom Printer",
|
||||
"version": "02.03.02.61",
|
||||
"version": "02.03.02.60",
|
||||
"force_update": "0",
|
||||
"description": "My configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
"from": "system",
|
||||
"instantiation": "true",
|
||||
"inherits": "fdm_filament_pet",
|
||||
"filament_type": "PP",
|
||||
"filament_type": [
|
||||
"PP"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"185"
|
||||
],
|
||||
|
||||
@@ -360,6 +360,50 @@ CONFLICT_KEYS = [
|
||||
['extruder_clearance_radius', 'extruder_clearance_max_radius'],
|
||||
]
|
||||
|
||||
VECTOR_KEYS = {
|
||||
"filament_type",
|
||||
}
|
||||
|
||||
def check_vector_type_keys(profiles_dir, vendor_name):
|
||||
"""
|
||||
Check that properties expected to be vectors (JSON arrays) are not stored as scalars.
|
||||
For example, `filament_type` must be a list like ["PA-CF"], not a string "PA-CF".
|
||||
|
||||
Parameters:
|
||||
profiles_dir (Path): Base profiles directory
|
||||
vendor_name (str): Vendor name
|
||||
|
||||
Returns:
|
||||
int: Number of errors found
|
||||
"""
|
||||
error_count = 0
|
||||
vendor_path = profiles_dir / vendor_name
|
||||
|
||||
if not vendor_path.exists():
|
||||
return 0
|
||||
|
||||
for file_path in vendor_path.rglob("*.json"):
|
||||
try:
|
||||
with open(file_path, "r", encoding="UTF-8") as fp:
|
||||
data = json.load(fp)
|
||||
except Exception as e:
|
||||
print_error(f"Error processing {file_path.relative_to(profiles_dir)}: {e}")
|
||||
error_count += 1
|
||||
continue
|
||||
|
||||
if not isinstance(data, dict):
|
||||
continue
|
||||
|
||||
for key in VECTOR_KEYS:
|
||||
if key in data and not isinstance(data[key], list):
|
||||
print_error(
|
||||
f"'{key}' must be an array in {file_path.relative_to(profiles_dir)}, "
|
||||
f"got {type(data[key]).__name__}: {data[key]!r}"
|
||||
)
|
||||
error_count += 1
|
||||
|
||||
return error_count
|
||||
|
||||
def check_conflict_keys(profiles_dir, vendor_name):
|
||||
"""
|
||||
Check for keys that could not be specified at the same time,
|
||||
@@ -448,6 +492,8 @@ def main():
|
||||
errors_found += new_errors
|
||||
warnings_found += new_warnings
|
||||
|
||||
errors_found += check_vector_type_keys(profiles_dir, vendor_name)
|
||||
|
||||
errors_found += check_filament_id(vendor_name, vendor_path / "filament")
|
||||
checked_vendor_count += 1
|
||||
|
||||
|
||||
@@ -66,9 +66,240 @@ static std::unique_ptr<noise::module::Module> get_noise_module(const FuzzySkinCo
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ripple noise — deterministic sine-wave displacement along the path arc length.
|
||||
//
|
||||
// Unlike the other noise types, the ripple pattern is driven by cumulative arc
|
||||
// length along the print path rather than world-space (x, y, z) coordinates.
|
||||
// This gives a uniform wave period regardless of the polygon's geometry.
|
||||
//
|
||||
// A consistent visual anchor is established by finding the leftmost Y=0 crossing
|
||||
// of the polygon (the point where the sine wave always peaks when phase shift is
|
||||
// zero), ensuring the pattern aligns across layers.
|
||||
//
|
||||
// Per-layer-group phase shifting works as follows:
|
||||
// period_index = floor(layer_id / layers_between_ripple_offset)
|
||||
// phase_shift = period_index * ripple_offset * 2π [radians]
|
||||
//
|
||||
// Setting layers_between_ripple_offset = 1 shifts the phase on every layer;
|
||||
// setting it to N makes N consecutive layers share the same pattern.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Compute the per-layer-group phase shift in radians.
|
||||
static double ripple_phase_shift_rad(const FuzzySkinConfig& cfg)
|
||||
{
|
||||
if (cfg.ripple_offset == 0.0 || cfg.layers_between_ripple_offset <= 0)
|
||||
return 0.0;
|
||||
|
||||
const int effective_layer = std::max(cfg.layer_id, 0);
|
||||
const int period_index = effective_layer / std::max(cfg.layers_between_ripple_offset, 1);
|
||||
const double raw_shift = period_index * cfg.ripple_offset * (2.0 * M_PI);
|
||||
return fmod(raw_shift, 2.0 * M_PI);
|
||||
}
|
||||
|
||||
// Find the arc-length (in mm) of the visual anchor point along the polygon perimeter.
|
||||
// The anchor is the leftmost Y=0 crossing, falling back to the vertex with the
|
||||
// smallest |y| if no crossing exists. The anchor is where sin(phase) = 1 (a peak)
|
||||
// when the phase shift is zero, giving a stable reference across layers.
|
||||
static double ripple_anchor_arc_mm(const Points& poly)
|
||||
{
|
||||
const size_t np = poly.size();
|
||||
|
||||
// Find anchor world position: leftmost Y=0 crossing.
|
||||
Vec2d anchor_world(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
|
||||
bool found_crossing = false;
|
||||
for (size_t i = 0; i < np; ++i) {
|
||||
const double ya = unscale_(poly[i].y());
|
||||
const double yb = unscale_(poly[(i + 1) % np].y());
|
||||
if ((ya <= 0.0 && yb >= 0.0) || (ya >= 0.0 && yb <= 0.0)) {
|
||||
const double t = (std::abs(yb - ya) < 1e-9) ? 0.0 : ya / (ya - yb);
|
||||
const double x_cross = unscale_(poly[i].x()) +
|
||||
std::max(0.0, std::min(1.0, t)) * (unscale_(poly[(i + 1) % np].x()) - unscale_(poly[i].x()));
|
||||
if (!found_crossing || x_cross < anchor_world.x()) {
|
||||
anchor_world = Vec2d(x_cross, 0.0);
|
||||
found_crossing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found_crossing) {
|
||||
double best_abs_y = std::numeric_limits<double>::max();
|
||||
for (const Point& p : poly) {
|
||||
const double ay = std::abs(unscale_(p.y()));
|
||||
if (ay < best_abs_y) {
|
||||
best_abs_y = ay;
|
||||
anchor_world = Vec2d(unscale_(p.x()), unscale_(p.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the arc-length of the closest point on the polyline to anchor_world.
|
||||
double anchor_arc_mm = 0.0;
|
||||
double best_dist_sq = std::numeric_limits<double>::max();
|
||||
double accum_mm = 0.0;
|
||||
for (size_t i = 0; i < np; ++i) {
|
||||
const Vec2d pa_mm(unscale_(poly[i].x()), unscale_(poly[i].y()));
|
||||
const Vec2d pb_mm(unscale_(poly[(i + 1) % np].x()), unscale_(poly[(i + 1) % np].y()));
|
||||
const Vec2d seg = pb_mm - pa_mm;
|
||||
const double seg_len = seg.norm();
|
||||
if (seg_len > 1e-9) {
|
||||
const double t = std::max(0.0, std::min(1.0, (anchor_world - pa_mm).dot(seg) / (seg_len * seg_len)));
|
||||
const double dist_sq = (pa_mm + seg * t - anchor_world).squaredNorm();
|
||||
if (dist_sq < best_dist_sq) {
|
||||
best_dist_sq = dist_sq;
|
||||
anchor_arc_mm = accum_mm + t * seg_len;
|
||||
}
|
||||
}
|
||||
accum_mm += seg_len;
|
||||
}
|
||||
return anchor_arc_mm;
|
||||
}
|
||||
|
||||
// Apply a sine-wave ripple displacement to a closed polygon.
|
||||
// Points are resampled at cfg.point_distance intervals along the perimeter.
|
||||
static void fuzzy_polyline_ripple(Points& poly, const FuzzySkinConfig& cfg)
|
||||
{
|
||||
const double amplitude = unscale_(cfg.thickness);
|
||||
const double N = static_cast<double>(cfg.ripples_per_layer);
|
||||
const double fill_step_mm = unscale_(cfg.point_distance);
|
||||
|
||||
if (N <= 0.0 || fill_step_mm < 1e-6)
|
||||
return;
|
||||
|
||||
// Compute total perimeter length in mm.
|
||||
const size_t np = poly.size();
|
||||
double perimeter_mm = 0.0;
|
||||
for (size_t i = 0; i < np; ++i)
|
||||
perimeter_mm += unscale_((poly[(i + 1) % np] - poly[i]).cast<double>().norm());
|
||||
|
||||
if (perimeter_mm < 1e-6)
|
||||
return;
|
||||
|
||||
const double anchor_arc_mm = ripple_anchor_arc_mm(poly);
|
||||
const double phase_shift_rad = ripple_phase_shift_rad(cfg);
|
||||
|
||||
// Phase function: φ(s) = N·2π·(s - anchor_arc) / perimeter + π/2 + phase_shift
|
||||
// Adding π/2 ensures sin(φ) = 1 at the anchor when phase_shift = 0 (a peak).
|
||||
const double phase_at_anchor = M_PI * 2.0 + phase_shift_rad;
|
||||
auto arc_phase = [&](double arc_mm) -> double { return N * (2.0 * M_PI) * (arc_mm - anchor_arc_mm) / perimeter_mm + phase_at_anchor; };
|
||||
|
||||
Points out;
|
||||
out.reserve(static_cast<size_t>(perimeter_mm / fill_step_mm) + np * 2);
|
||||
|
||||
double accum_mm = 0.0;
|
||||
for (size_t i = 0; i < np; ++i) {
|
||||
const Point& p0 = poly[i];
|
||||
const Point& p1 = poly[(i + 1) % np];
|
||||
const Vec2d seg = (p1 - p0).cast<double>();
|
||||
const double seg_len = seg.norm();
|
||||
if (seg_len < EPSILON)
|
||||
continue;
|
||||
|
||||
const double seg_len_mm = unscale_(seg_len);
|
||||
const Vec2d seg_unit = seg / seg_len;
|
||||
const Vec2d seg_perp = perp(seg_unit);
|
||||
const double seg_end_mm = accum_mm + seg_len_mm;
|
||||
const double first_s = std::ceil(accum_mm / fill_step_mm) * fill_step_mm;
|
||||
|
||||
for (double s = first_s; s < seg_end_mm; s += fill_step_mm) {
|
||||
const double t = (s - accum_mm) / seg_len_mm;
|
||||
const double disp = std::sin(arc_phase(s)) * amplitude;
|
||||
const Point pt = p0 + (seg * t).cast<coord_t>();
|
||||
out.emplace_back(pt + (seg_perp * scale_(disp)).cast<coord_t>());
|
||||
}
|
||||
|
||||
accum_mm = seg_end_mm;
|
||||
}
|
||||
|
||||
while (out.size() < 3)
|
||||
out.emplace_back(poly[poly.size() - 2]);
|
||||
|
||||
if (out.size() >= 3)
|
||||
poly = std::move(out);
|
||||
}
|
||||
|
||||
// Apply a sine-wave ripple displacement to an Arachne extrusion line.
|
||||
// Mirrors fuzzy_polyline_ripple but operates on ExtrusionJunction vectors so
|
||||
// that per-point line width (j.w) is preserved correctly.
|
||||
static void fuzzy_extrusion_line_ripple(Arachne::ExtrusionJunctions& ext_lines, const FuzzySkinConfig& cfg)
|
||||
{
|
||||
const double amplitude = unscale_(cfg.thickness);
|
||||
const double N = static_cast<double>(cfg.ripples_per_layer);
|
||||
const double fill_step_mm = unscale_(cfg.point_distance);
|
||||
|
||||
if (N <= 0.0 || fill_step_mm < 1e-6)
|
||||
return;
|
||||
|
||||
// Build a Points vector for perimeter/anchor calculations.
|
||||
Points poly;
|
||||
poly.reserve(ext_lines.size());
|
||||
for (const auto& j : ext_lines)
|
||||
poly.push_back(j.p);
|
||||
|
||||
// Compute total length in mm.
|
||||
const size_t np = poly.size();
|
||||
double perimeter_mm = 0.0;
|
||||
for (size_t i = 0; i + 1 < np; ++i)
|
||||
perimeter_mm += unscale_((poly[i + 1] - poly[i]).cast<double>().norm());
|
||||
|
||||
if (perimeter_mm < 1e-6)
|
||||
return;
|
||||
|
||||
const double anchor_arc_mm = ripple_anchor_arc_mm(poly);
|
||||
const double phase_shift_rad = ripple_phase_shift_rad(cfg);
|
||||
const double phase_at_anchor = M_PI * 2.0 + phase_shift_rad;
|
||||
|
||||
auto arc_phase = [&](double arc_mm) -> double { return N * (2.0 * M_PI) * (arc_mm - anchor_arc_mm) / perimeter_mm + phase_at_anchor; };
|
||||
|
||||
Arachne::ExtrusionJunctions out;
|
||||
out.reserve(static_cast<size_t>(perimeter_mm / fill_step_mm) + np * 2);
|
||||
|
||||
double accum_mm = 0.0;
|
||||
for (size_t i = 0; i + 1 < np; ++i) {
|
||||
const Arachne::ExtrusionJunction& j0 = ext_lines[i];
|
||||
const Arachne::ExtrusionJunction& j1 = ext_lines[i + 1];
|
||||
const Vec2d seg = (j1.p - j0.p).cast<double>();
|
||||
const double seg_len = seg.norm();
|
||||
if (seg_len < EPSILON)
|
||||
continue;
|
||||
|
||||
const double seg_len_mm = unscale_(seg_len);
|
||||
const Vec2d seg_unit = seg / seg_len;
|
||||
const Vec2d seg_perp = perp(seg_unit);
|
||||
const double seg_end_mm = accum_mm + seg_len_mm;
|
||||
const double first_s = std::ceil(accum_mm / fill_step_mm) * fill_step_mm;
|
||||
|
||||
for (double s = first_s; s < seg_end_mm; s += fill_step_mm) {
|
||||
const double t = (s - accum_mm) / seg_len_mm;
|
||||
const double disp = std::sin(arc_phase(s)) * amplitude;
|
||||
const Point pt = j0.p + (seg * t).cast<coord_t>();
|
||||
out.emplace_back(pt + (seg_perp * scale_(disp)).cast<coord_t>(), j1.w, j1.perimeter_index);
|
||||
}
|
||||
|
||||
accum_mm = seg_end_mm;
|
||||
}
|
||||
|
||||
while (out.size() < 3) {
|
||||
size_t point_idx = ext_lines.size() - 2;
|
||||
out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index);
|
||||
if (point_idx == 0)
|
||||
break;
|
||||
--point_idx;
|
||||
}
|
||||
|
||||
if (out.size() >= 3)
|
||||
ext_lines = std::move(out);
|
||||
}
|
||||
|
||||
// Thanks Cura developers for this function.
|
||||
void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkinConfig& cfg)
|
||||
{
|
||||
if (cfg.noise_type == NoiseType::Ripple) {
|
||||
if (poly.size() < 3)
|
||||
return;
|
||||
fuzzy_polyline_ripple(poly, cfg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<noise::module::Module> noise = get_noise_module(cfg);
|
||||
|
||||
const double min_dist_between_points = cfg.point_distance * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
@@ -113,6 +344,14 @@ void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkin
|
||||
// Thanks Cura developers for this function.
|
||||
void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg, bool closed)
|
||||
{
|
||||
|
||||
if (cfg.noise_type == NoiseType::Ripple) {
|
||||
if (ext_lines.size() < 3)
|
||||
return;
|
||||
fuzzy_extrusion_line_ripple(ext_lines, cfg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<noise::module::Module> noise = get_noise_module(cfg);
|
||||
|
||||
const double min_dist_between_points = cfg.point_distance * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
@@ -190,7 +429,11 @@ void group_region_by_fuzzify(PerimeterGenerator& g)
|
||||
region_config.fuzzy_skin_scale,
|
||||
region_config.fuzzy_skin_octaves,
|
||||
region_config.fuzzy_skin_persistence,
|
||||
region_config.fuzzy_skin_mode};
|
||||
region_config.fuzzy_skin_mode,
|
||||
region_config.fuzzy_skin_ripples_per_layer,
|
||||
region_config.fuzzy_skin_ripple_offset,
|
||||
region_config.fuzzy_skin_layers_between_ripple_offset,
|
||||
g.layer_id};
|
||||
auto& surfaces = regions[cfg];
|
||||
for (const auto& surface : region->slices.surfaces) {
|
||||
surfaces.push_back(&surface);
|
||||
|
||||
@@ -21,6 +21,10 @@ struct FuzzySkinConfig
|
||||
int noise_octaves;
|
||||
double noise_persistence;
|
||||
FuzzySkinMode mode;
|
||||
int ripples_per_layer;
|
||||
double ripple_offset;
|
||||
int layers_between_ripple_offset;
|
||||
int layer_id;
|
||||
|
||||
bool operator==(const FuzzySkinConfig& r) const
|
||||
{
|
||||
@@ -32,7 +36,10 @@ struct FuzzySkinConfig
|
||||
&& noise_scale == r.noise_scale
|
||||
&& noise_octaves == r.noise_octaves
|
||||
&& noise_persistence == r.noise_persistence
|
||||
&& mode == r.mode;
|
||||
&& mode == r.mode
|
||||
&& ripples_per_layer == r.ripples_per_layer
|
||||
&& ripple_offset == r.ripple_offset
|
||||
&& layers_between_ripple_offset == r.layers_between_ripple_offset;
|
||||
}
|
||||
|
||||
bool operator!=(const FuzzySkinConfig& r) const { return !(*this == r); }
|
||||
@@ -52,6 +59,10 @@ template<> struct hash<Slic3r::FuzzySkinConfig>
|
||||
boost::hash_combine(seed, std::hash<double>{}(c.noise_scale));
|
||||
boost::hash_combine(seed, std::hash<int>{}(c.noise_octaves));
|
||||
boost::hash_combine(seed, std::hash<double>{}(c.noise_persistence));
|
||||
boost::hash_combine(seed, std::hash<Slic3r::FuzzySkinMode>{}(c.mode));
|
||||
boost::hash_combine(seed, std::hash<int>{}(c.ripples_per_layer));
|
||||
boost::hash_combine(seed, std::hash<double>{}(c.ripple_offset));
|
||||
boost::hash_combine(seed, std::hash<int>{}(c.layers_between_ripple_offset));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1043,7 +1043,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"support_ironing_flow",
|
||||
"support_ironing_spacing",
|
||||
"max_travel_detour_distance",
|
||||
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence",
|
||||
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "fuzzy_skin_ripples_per_layer", "fuzzy_skin_ripple_offset", "fuzzy_skin_layers_between_ripple_offset",
|
||||
"max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only",
|
||||
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
|
||||
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed",
|
||||
|
||||
@@ -4549,6 +4549,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
std::string version_str = it.value();
|
||||
auto config_version = Semver::parse(version_str);
|
||||
if (! config_version) {
|
||||
++m_errors;
|
||||
throw ConfigurationError((boost::format("vendor %1%'s config version: %2% invalid\nSuggest cleaning the directory %3% firstly")
|
||||
% vendor_name % version_str % path).str());
|
||||
} else {
|
||||
|
||||
@@ -183,7 +183,8 @@ static t_config_enum_values s_keys_map_NoiseType {
|
||||
{ "perlin", int(NoiseType::Perlin) },
|
||||
{ "billow", int(NoiseType::Billow) },
|
||||
{ "ridgedmulti", int(NoiseType::RidgedMulti) },
|
||||
{ "voronoi", int(NoiseType::Voronoi) }
|
||||
{ "voronoi", int(NoiseType::Voronoi) },
|
||||
{ "ripple", int(NoiseType::Ripple) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NoiseType)
|
||||
|
||||
@@ -3370,7 +3371,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->tooltip = L("The width within which to jitter. It's advised to be below outer wall line width.");
|
||||
def->sidetext = L("mm"); // millimeters, CIS languages need translation
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->max = 2;
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
@@ -3422,18 +3423,21 @@ void PrintConfigDef::init_fff_params()
|
||||
"Perlin: Perlin noise, which gives a more consistent texture.\n"
|
||||
"Billow: Similar to perlin noise, but clumpier.\n"
|
||||
"Ridged Multifractal: Ridged noise with sharp, jagged features. Creates marble-like textures.\n"
|
||||
"Voronoi: Divides the surface into voronoi cells, and displaces each one by a random amount. Creates a patchwork texture.");
|
||||
"Voronoi: Divides the surface into voronoi cells, and displaces each one by a random amount. Creates a patchwork texture.\n"
|
||||
"Ripple: Uniform ripple pattern that ripples left and right of the original path. Repeating pattern, woven appearance.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<NoiseType>::get_enum_values();
|
||||
def->enum_values.push_back("classic");
|
||||
def->enum_values.push_back("perlin");
|
||||
def->enum_values.push_back("billow");
|
||||
def->enum_values.push_back("ridgedmulti");
|
||||
def->enum_values.push_back("voronoi");
|
||||
def->enum_values.push_back("ripple");
|
||||
def->enum_labels.push_back(L("Classic"));
|
||||
def->enum_labels.push_back(L("Perlin"));
|
||||
def->enum_labels.push_back(L("Billow"));
|
||||
def->enum_labels.push_back(L("Ridged Multifractal"));
|
||||
def->enum_labels.push_back(L("Voronoi"));
|
||||
def->enum_labels.push_back(L("Ripple"));
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnum<NoiseType>(NoiseType::Classic));
|
||||
|
||||
@@ -3465,6 +3469,38 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.5));
|
||||
|
||||
def = this->add("fuzzy_skin_ripples_per_layer", coInt);
|
||||
def->label = L("Number of ripples per layer");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("When using the Ripple noise type, this controls how many full cycles of ripples will be added per layer.");
|
||||
def->min = 1;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(15));
|
||||
|
||||
def = this->add("fuzzy_skin_ripple_offset", coFloat);
|
||||
def->label = L("Ripple offset");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("When using the Ripple noise type, shifts the ripple pattern forward along the print path by this amount each "
|
||||
"layer-period. A value of 0 keeps every layer identical. A value equal to 0.5 shifts by a full "
|
||||
"half-wavelength, inverting the pattern. The shift is applied once per 'Layers between Ripple offset' layers, "
|
||||
"so consecutive layers within a period are printed identically on top of each other.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.5));
|
||||
|
||||
def = this->add("fuzzy_skin_layers_between_ripple_offset", coInt);
|
||||
def->label = L("Layers between ripple offset");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("When using the Ripple noise type with a non-zero layer offset, this controls how "
|
||||
"many consecutive layers share the same ripple phase before the offset is applied. "
|
||||
"For example, a period of 3 means layers 0, 1 and 2 are identical, then layers 3, 4 "
|
||||
"and 5 are shifted by one full 'Ripple layer offset', and so on. "
|
||||
"Set to 1 to shift on every layer.");
|
||||
def->min = 1;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
def = this->add("filter_out_gap_fill", coFloat);
|
||||
def->label = L("Filter out tiny gaps");
|
||||
def->category = L("Layers and Perimeters");
|
||||
|
||||
@@ -56,6 +56,7 @@ enum class NoiseType {
|
||||
Billow,
|
||||
RidgedMulti,
|
||||
Voronoi,
|
||||
Ripple,
|
||||
};
|
||||
|
||||
enum class WipeTowerType {
|
||||
@@ -1084,6 +1085,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, fuzzy_skin_scale))
|
||||
((ConfigOptionInt, fuzzy_skin_octaves))
|
||||
((ConfigOptionFloat, fuzzy_skin_persistence))
|
||||
((ConfigOptionInt, fuzzy_skin_ripples_per_layer))
|
||||
((ConfigOptionFloat, fuzzy_skin_ripple_offset))
|
||||
((ConfigOptionInt, fuzzy_skin_layers_between_ripple_offset))
|
||||
((ConfigOptionFloat, gap_infill_speed))
|
||||
((ConfigOptionInt, sparse_infill_filament))
|
||||
((ConfigOptionFloatOrPercent, sparse_infill_line_width))
|
||||
|
||||
@@ -881,9 +881,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
// Show noise type specific options with the same logic
|
||||
NoiseType fuzzy_skin_noise_type = config->opt_enum<NoiseType>("fuzzy_skin_noise_type");
|
||||
toggle_line("fuzzy_skin_scale", fuzzy_skin_noise_type != NoiseType::Classic && has_fuzzy_skin);
|
||||
toggle_line("fuzzy_skin_octaves", fuzzy_skin_noise_type != NoiseType::Classic && fuzzy_skin_noise_type != NoiseType::Voronoi && has_fuzzy_skin);
|
||||
toggle_line("fuzzy_skin_persistence", (fuzzy_skin_noise_type == NoiseType::Perlin || fuzzy_skin_noise_type == NoiseType::Billow) && has_fuzzy_skin);
|
||||
const bool is_ripple = fuzzy_skin_noise_type == NoiseType::Ripple;
|
||||
toggle_line("fuzzy_skin_scale", fuzzy_skin_noise_type != NoiseType::Classic && has_fuzzy_skin && !is_ripple);
|
||||
toggle_line("fuzzy_skin_octaves", fuzzy_skin_noise_type != NoiseType::Classic && fuzzy_skin_noise_type != NoiseType::Voronoi && has_fuzzy_skin && !is_ripple);
|
||||
toggle_line("fuzzy_skin_persistence", (fuzzy_skin_noise_type == NoiseType::Perlin || fuzzy_skin_noise_type == NoiseType::Billow) && has_fuzzy_skin && !is_ripple);
|
||||
toggle_line("fuzzy_skin_ripples_per_layer", is_ripple && has_fuzzy_skin);
|
||||
toggle_line("fuzzy_skin_ripple_offset", is_ripple && has_fuzzy_skin);
|
||||
toggle_line("fuzzy_skin_layers_between_ripple_offset", is_ripple && has_fuzzy_skin);
|
||||
|
||||
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
|
||||
for (auto el : {"wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_length_factor",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iterator>
|
||||
#include <exception>
|
||||
#include <cstdlib>
|
||||
@@ -6473,6 +6474,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
ProgressFn progressFn;
|
||||
WasCancelledFn cancelFn;
|
||||
std::function<void(bool)> finishFn;
|
||||
auto preset_resolution_pending = std::make_shared<std::atomic_bool>(false);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "start_sync_service...";
|
||||
// BBS
|
||||
@@ -6488,17 +6490,23 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
cancelFn = [this, dlg]() {
|
||||
return is_closing() || dlg->WasCanceled();
|
||||
};
|
||||
finishFn = [this, userid = m_agent->get_user_id(), dlg, t = std::weak_ptr<int>(m_user_sync_token)](bool ok) {
|
||||
finishFn = [this, userid = m_agent->get_user_id(), dlg, t = std::weak_ptr<int>(m_user_sync_token), preset_resolution_pending](bool ok) {
|
||||
if (ok)
|
||||
preset_resolution_pending->store(true);
|
||||
CallAfter([=]{
|
||||
dlg->Destroy();
|
||||
if (ok && m_agent && t.lock() == m_user_sync_token && userid == m_agent->get_user_id()) reload_settings();
|
||||
preset_resolution_pending->store(false);
|
||||
});
|
||||
};
|
||||
}
|
||||
else {
|
||||
finishFn = [this, userid = m_agent->get_user_id(), t = std::weak_ptr<int>(m_user_sync_token)](bool ok) {
|
||||
finishFn = [this, userid = m_agent->get_user_id(), t = std::weak_ptr<int>(m_user_sync_token), preset_resolution_pending](bool ok) {
|
||||
if (ok)
|
||||
preset_resolution_pending->store(true);
|
||||
CallAfter([=] {
|
||||
if (ok && m_agent && t.lock() == m_user_sync_token && userid == m_agent->get_user_id()) reload_settings();
|
||||
preset_resolution_pending->store(false);
|
||||
});
|
||||
};
|
||||
cancelFn = [this]() {
|
||||
@@ -6506,6 +6514,138 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
};
|
||||
}
|
||||
|
||||
struct CloudPresetChange
|
||||
{
|
||||
std::string type;
|
||||
std::string name;
|
||||
std::string setting_id;
|
||||
long long update_time {0};
|
||||
bool deleted {false};
|
||||
};
|
||||
|
||||
auto make_setting_check_fn = [this](bool *need_reload = nullptr, std::vector<CloudPresetChange> *incoming_changes = nullptr) -> CheckFn {
|
||||
return [this, need_reload, incoming_changes](std::map<std::string, std::string> info) {
|
||||
auto value_or_empty = [&info](const std::string &key) -> std::string {
|
||||
auto it = info.find(key);
|
||||
return it == info.end() ? std::string() : it->second;
|
||||
};
|
||||
|
||||
if (info.find("deleted") != info.end()) {
|
||||
if (need_reload)
|
||||
*need_reload = true;
|
||||
if (incoming_changes) {
|
||||
CloudPresetChange change;
|
||||
change.setting_id = value_or_empty(BBL_JSON_KEY_SETTING_ID);
|
||||
change.deleted = true;
|
||||
incoming_changes->push_back(std::move(change));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto type = value_or_empty(BBL_JSON_KEY_TYPE);
|
||||
auto name = value_or_empty(BBL_JSON_KEY_NAME);
|
||||
auto setting_id = value_or_empty(BBL_JSON_KEY_SETTING_ID);
|
||||
auto update_time_str = value_or_empty(ORCA_JSON_KEY_UPDATE_TIME);
|
||||
long long update_time = 0;
|
||||
if (!update_time_str.empty())
|
||||
update_time = std::atoll(update_time_str.c_str());
|
||||
|
||||
bool need_sync = true;
|
||||
if (preset_bundle && type == "filament") {
|
||||
need_sync = preset_bundle->filaments.need_sync(name, setting_id, update_time);
|
||||
} else if (preset_bundle && type == "print") {
|
||||
need_sync = preset_bundle->prints.need_sync(name, setting_id, update_time);
|
||||
} else if (preset_bundle && type == "printer") {
|
||||
need_sync = preset_bundle->printers.need_sync(name, setting_id, update_time);
|
||||
}
|
||||
|
||||
if (need_sync) {
|
||||
if (need_reload)
|
||||
*need_reload = true;
|
||||
if (incoming_changes)
|
||||
incoming_changes->push_back({ type, name, setting_id, update_time, false });
|
||||
}
|
||||
return need_sync;
|
||||
};
|
||||
};
|
||||
|
||||
auto has_local_preset_changes = [this]() -> bool {
|
||||
if (!preset_bundle)
|
||||
return false;
|
||||
|
||||
auto collection_has_changes = [this](PresetCollection &presets) {
|
||||
if (presets.get_edited_preset().is_user() && presets.current_is_dirty())
|
||||
return true;
|
||||
|
||||
std::vector<Preset> presets_to_sync;
|
||||
return presets.get_user_presets(preset_bundle, presets_to_sync) > 0;
|
||||
};
|
||||
|
||||
return collection_has_changes(preset_bundle->prints) ||
|
||||
collection_has_changes(preset_bundle->filaments) ||
|
||||
collection_has_changes(preset_bundle->printers);
|
||||
};
|
||||
|
||||
auto save_dirty_user_preset_for_sync = [this](PresetCollection &presets) {
|
||||
if (!preset_bundle || !presets.get_edited_preset().is_user() || !presets.current_is_dirty())
|
||||
return;
|
||||
|
||||
const std::string name = presets.get_edited_preset().name;
|
||||
presets.save_current_preset(name, false, false, nullptr);
|
||||
if (Preset *preset = presets.find_preset(name, false, true)) {
|
||||
preset->sync_info = preset->setting_id.empty() ? "create" : "update";
|
||||
if (m_agent)
|
||||
preset->user_id = m_agent->get_user_id();
|
||||
preset->save_info();
|
||||
}
|
||||
};
|
||||
|
||||
auto keep_deleted_cloud_preset_local = [this](PresetCollection &presets, const std::string &setting_id) -> bool {
|
||||
if (setting_id.empty())
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
presets.lock();
|
||||
for (Preset &preset : presets) {
|
||||
if (preset.is_user() && preset.setting_id == setting_id) {
|
||||
preset.setting_id.clear();
|
||||
preset.sync_info = "create";
|
||||
if (m_agent)
|
||||
preset.user_id = m_agent->get_user_id();
|
||||
preset.save_info();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
presets.unlock();
|
||||
return found;
|
||||
};
|
||||
|
||||
auto keep_cloud_changes_local = [this, save_dirty_user_preset_for_sync, keep_deleted_cloud_preset_local](const std::vector<CloudPresetChange> &incoming_changes) {
|
||||
if (!preset_bundle)
|
||||
return;
|
||||
|
||||
save_dirty_user_preset_for_sync(preset_bundle->prints);
|
||||
save_dirty_user_preset_for_sync(preset_bundle->filaments);
|
||||
save_dirty_user_preset_for_sync(preset_bundle->printers);
|
||||
|
||||
for (const CloudPresetChange &change : incoming_changes) {
|
||||
if (change.deleted) {
|
||||
if (!keep_deleted_cloud_preset_local(preset_bundle->prints, change.setting_id) &&
|
||||
!keep_deleted_cloud_preset_local(preset_bundle->filaments, change.setting_id))
|
||||
keep_deleted_cloud_preset_local(preset_bundle->printers, change.setting_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (change.type == "filament")
|
||||
preset_bundle->filaments.set_sync_info_and_save(change.name, change.setting_id, "update", change.update_time);
|
||||
else if (change.type == "print")
|
||||
preset_bundle->prints.set_sync_info_and_save(change.name, change.setting_id, "update", change.update_time);
|
||||
else if (change.type == "printer")
|
||||
preset_bundle->printers.set_sync_info_and_save(change.name, change.setting_id, "update", change.update_time);
|
||||
}
|
||||
};
|
||||
|
||||
// Do a one-time scan for files that may be pending deletion (e.g., was deleted while not connected to internet)
|
||||
// Scan for orphaned .info files on startup and add them to deletion queue
|
||||
scan_orphaned_info_files();
|
||||
@@ -6514,7 +6654,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
Bind(EVT_UPDATE_PRESET_BUNDLE,&GUI_App::update_single_bundle,this);
|
||||
|
||||
m_sync_update_thread = Slic3r::create_thread(
|
||||
[this, progressFn, cancelFn, finishFn, t = std::weak_ptr<int>(m_user_sync_token)] {
|
||||
[this, progressFn, cancelFn, finishFn, make_setting_check_fn, has_local_preset_changes, keep_cloud_changes_local, preset_resolution_pending, t = std::weak_ptr<int>(m_user_sync_token)] {
|
||||
// get setting list, update setting list
|
||||
std::string version = preset_bundle->get_vendor_profile_version(PresetBundle::ORCA_DEFAULT_BUNDLE).to_string();
|
||||
if(!m_agent) return;
|
||||
@@ -6523,24 +6663,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
// So that we can sync presets that are migrated from old version or users manually put preset files in preset folder
|
||||
preset_bundle->check_and_fix_user_presets_syncinfo(m_agent->get_user_id());
|
||||
|
||||
int ret = m_agent->get_setting_list2(version, [this](auto info) {
|
||||
auto type = info[BBL_JSON_KEY_TYPE];
|
||||
auto name = info[BBL_JSON_KEY_NAME];
|
||||
auto setting_id = info[BBL_JSON_KEY_SETTING_ID];
|
||||
auto update_time_str = info[ORCA_JSON_KEY_UPDATE_TIME];
|
||||
long long update_time = 0;
|
||||
if (!update_time_str.empty())
|
||||
update_time = std::atoll(update_time_str.c_str());
|
||||
if (type == "filament") {
|
||||
return preset_bundle->filaments.need_sync(name, setting_id, update_time);
|
||||
} else if (type == "print") {
|
||||
return preset_bundle->prints.need_sync(name, setting_id, update_time);
|
||||
} else if (type == "printer") {
|
||||
return preset_bundle->printers.need_sync(name, setting_id, update_time);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}, progressFn, cancelFn);
|
||||
int ret = m_agent->get_setting_list2(version, make_setting_check_fn(), progressFn, cancelFn);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " get_setting_list2 ret = " << ret << " m_is_closing = " << m_is_closing;
|
||||
|
||||
finishFn(ret == 0);
|
||||
@@ -6563,42 +6686,89 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
|
||||
//sync preset
|
||||
if (!preset_bundle) continue;
|
||||
|
||||
int total_count = 0;
|
||||
sync_count = preset_bundle->prints.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
bool cloud_presets_need_reload = false;
|
||||
std::vector<CloudPresetChange> incoming_changes;
|
||||
int pull_ret = m_agent->get_setting_list2(
|
||||
version,
|
||||
make_setting_check_fn(&cloud_presets_need_reload, &incoming_changes),
|
||||
nullptr,
|
||||
[this]() { return is_closing(); });
|
||||
BOOST_LOG_TRIVIAL(info) << "start_sync_user_preset: periodic get_setting_list2 ret = " << pull_ret
|
||||
<< ", cloud_presets_need_reload = " << cloud_presets_need_reload;
|
||||
bool skip_user_preset_upload = preset_resolution_pending->load();
|
||||
if (pull_ret == 0 && cloud_presets_need_reload) {
|
||||
skip_user_preset_upload = true;
|
||||
if (!preset_resolution_pending->exchange(true)) {
|
||||
CallAfter([this, incoming_changes, has_local_preset_changes, keep_cloud_changes_local, preset_resolution_pending, t] {
|
||||
if (is_closing() || !m_agent || t.expired() || t.lock() != m_user_sync_token) {
|
||||
preset_resolution_pending->store(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_local_preset_changes()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "start_sync_user_preset: applying incoming cloud preset changes";
|
||||
reload_settings();
|
||||
preset_resolution_pending->store(false);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageDialog dlg(mainframe,
|
||||
_L("Cloud presets were updated while you have local preset changes.\n\nChoose which version to keep. This decision is final."),
|
||||
_L("Preset sync conflict"),
|
||||
wxYES_NO | wxYES_DEFAULT | wxICON_WARNING);
|
||||
dlg.SetButtonLabel(wxID_YES, _L("Use incoming"));
|
||||
dlg.SetButtonLabel(wxID_NO, _L("Keep local"));
|
||||
int answer = dlg.ShowModal();
|
||||
if (answer == wxID_YES) {
|
||||
BOOST_LOG_TRIVIAL(info) << "start_sync_user_preset: user chose incoming cloud presets";
|
||||
reload_settings();
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "start_sync_user_preset: user chose local presets";
|
||||
keep_cloud_changes_local(incoming_changes);
|
||||
}
|
||||
preset_resolution_pending->store(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
total_count += sync_count;
|
||||
|
||||
sync_count = preset_bundle->filaments.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
if (!skip_user_preset_upload) {
|
||||
int total_count = 0;
|
||||
sync_count = preset_bundle->prints.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
total_count += sync_count;
|
||||
total_count += sync_count;
|
||||
|
||||
sync_count = preset_bundle->printers.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
sync_count = preset_bundle->filaments.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
total_count += sync_count;
|
||||
total_count += sync_count;
|
||||
|
||||
if (total_count == 0) {
|
||||
CallAfter([this] {
|
||||
if (!is_closing())
|
||||
plater()->get_notification_manager()->close_notification_of_type(NotificationType::BBLUserPresetExceedLimit);
|
||||
});
|
||||
}
|
||||
sync_count = preset_bundle->printers.get_user_presets(preset_bundle, presets_to_sync);
|
||||
if (sync_count > 0) {
|
||||
for (Preset& preset : presets_to_sync) {
|
||||
sync_preset(&preset);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
total_count += sync_count;
|
||||
|
||||
process_delete_presets();
|
||||
if (total_count == 0) {
|
||||
CallAfter([this] {
|
||||
if (!is_closing())
|
||||
plater()->get_notification_manager()->close_notification_of_type(NotificationType::BBLUserPresetExceedLimit);
|
||||
});
|
||||
}
|
||||
|
||||
process_delete_presets();
|
||||
}
|
||||
}
|
||||
|
||||
// sync subscribed bundles, if orca
|
||||
|
||||
@@ -1355,16 +1355,16 @@ void PlaterPresetComboBox::update()
|
||||
bool unsupported = group == "Unsupported presets";
|
||||
for (auto it : list) {
|
||||
// ORCA add sorting support for vendor / type for user presets
|
||||
auto groupName2 = groupName == "by_bundle" ? (preset_bundle_names[it->first].empty() ? _L("Unspecified") : preset_bundle_names[it->first])
|
||||
: groupName == "by_type" ? (preset_filament_types[it->first].empty() ? _L("Unspecified") : preset_filament_types[it->first])
|
||||
: groupName == "by_vendor" ? (preset_filament_vendors[it->first].empty() ? _L("Unspecified") : preset_filament_vendors[it->first])
|
||||
auto groupName2 = groupName == "by_bundle" ? (preset_bundle_names[it->first].empty() ? _L("Unspecified") : from_u8(preset_bundle_names[it->first]))
|
||||
: groupName == "by_type" ? (preset_filament_types[it->first].empty() ? _L("Unspecified") : from_u8(preset_filament_types[it->first]))
|
||||
: groupName == "by_vendor" ? (preset_filament_vendors[it->first].empty() ? _L("Unspecified") : from_u8(preset_filament_vendors[it->first]))
|
||||
: groupByGroup ? groupName
|
||||
: preset_filament_vendors[it->first];
|
||||
: from_u8(preset_filament_vendors[it->first]);
|
||||
int index = groupName == "by_bundle"
|
||||
? Append(preset_aliases[it->first], *it->second,
|
||||
? Append(from_u8(preset_aliases[it->first]), *it->second,
|
||||
from_u8(preset_bundle_ids[it->first]), groupName2, nullptr,
|
||||
unsupported ? DD_ITEM_STYLE_DISABLED : 0)
|
||||
: Append(preset_aliases[it->first], *it->second, groupName2, nullptr,
|
||||
: Append(from_u8(preset_aliases[it->first]), *it->second, groupName2, nullptr,
|
||||
unsupported ? DD_ITEM_STYLE_DISABLED : 0);
|
||||
SetItemAlias(index, it->first);
|
||||
if (unsupported)
|
||||
@@ -1380,7 +1380,7 @@ void PlaterPresetComboBox::update()
|
||||
}
|
||||
} else {
|
||||
for (std::map<wxString, wxBitmap *>::const_iterator it = presets.begin(); it != presets.end(); ++it) {
|
||||
int index = Append(preset_aliases[it->first], *it->second);
|
||||
int index = Append(from_u8(preset_aliases[it->first]), *it->second);
|
||||
SetItemAlias(index, it->first);
|
||||
SetItemTooltip(index, preset_descriptions[it->first]);
|
||||
if (group == "System presets")
|
||||
@@ -1767,10 +1767,10 @@ void TabPresetComboBox::update()
|
||||
// Get bundle name for grouping
|
||||
wxString bundle_name = _L("Unspecified");
|
||||
if (preset_bundle_names.count(it->first) > 0 && !preset_bundle_names[it->first].empty()) {
|
||||
bundle_name = preset_bundle_names[it->first];
|
||||
bundle_name = from_u8(preset_bundle_names[it->first]);
|
||||
}
|
||||
// Use Append with group parameter for sub-dropdown grouping
|
||||
int item_id = Append(preset_aliases[it->first], *it->second.first, from_u8(preset_bundle_ids[it->first]), bundle_name);
|
||||
int item_id = Append(from_u8(preset_aliases[it->first]), *it->second.first, from_u8(preset_bundle_ids[it->first]), bundle_name);
|
||||
SetItemAlias(item_id, it->first);
|
||||
SetItemTooltip(item_id, preset_descriptions[it->first]);
|
||||
bool is_enabled = it->second.second;
|
||||
|
||||
@@ -2677,6 +2677,9 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("fuzzy_skin_scale", "others_settings_fuzzy_skin#skin-feature-size");
|
||||
optgroup->append_single_option_line("fuzzy_skin_octaves", "others_settings_fuzzy_skin#skin-noise-octaves");
|
||||
optgroup->append_single_option_line("fuzzy_skin_persistence", "others_settings_fuzzy_skin#skin-noise-persistence");
|
||||
optgroup->append_single_option_line("fuzzy_skin_ripples_per_layer", "others_settings_fuzzy_skin#ripples-per-layer");
|
||||
optgroup->append_single_option_line("fuzzy_skin_ripple_offset", "others_settings_fuzzy_skin#ripple-offset");
|
||||
optgroup->append_single_option_line("fuzzy_skin_layers_between_ripple_offset", "others_settings_fuzzy_skin#layers-between-ripple-offset");
|
||||
optgroup->append_single_option_line("fuzzy_skin_first_layer", "others_settings_fuzzy_skin#apply-fuzzy-skin-to-first-layer");
|
||||
|
||||
optgroup = page->new_optgroup(L("G-code output"), L"param_gcode");
|
||||
|
||||
Reference in New Issue
Block a user