mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Merge branch 'main' into dev/bbl-network-upd
This commit is contained in:
@@ -178,6 +178,9 @@ void AppConfig::set_defaults()
|
||||
if (get("camera_navigation_style").empty())
|
||||
set("camera_navigation_style", "0");
|
||||
|
||||
if (get("swap_mouse_buttons").empty())
|
||||
set_bool("swap_mouse_buttons", false);
|
||||
|
||||
if (get("reverse_mouse_wheel_zoom").empty())
|
||||
set_bool("reverse_mouse_wheel_zoom", false);
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
|
||||
return out;
|
||||
}
|
||||
|
||||
BoundingBox BoundingBox::scaled(double factor) const
|
||||
{
|
||||
BoundingBox out(*this);
|
||||
out.scale(factor);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
||||
{
|
||||
|
||||
@@ -222,7 +222,9 @@ public:
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
|
||||
|
||||
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
|
||||
BoundingBox inflated(coordf_t delta) const noexcept { BoundingBox out(*this); out.offset(delta); return out; }
|
||||
|
||||
BoundingBox scaled(double factor) const;
|
||||
|
||||
friend BoundingBox get_extents_rotated(const Points &points, double angle);
|
||||
};
|
||||
|
||||
@@ -657,6 +657,11 @@ public:
|
||||
{
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
|
||||
if (str.empty()) {
|
||||
this->values.push_back(0);
|
||||
return true;
|
||||
}
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ',')) {
|
||||
@@ -675,6 +680,13 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool validate_string(const std::string &str)
|
||||
{
|
||||
// should only have number and commas
|
||||
return std::all_of(str.begin(), str.end(), [](char c) {
|
||||
return std::isdigit(c) || c == ','|| std::isspace(c);
|
||||
});
|
||||
}
|
||||
|
||||
ConfigOptionFloatsTempl& operator=(const ConfigOption *opt)
|
||||
{
|
||||
|
||||
+551
-544
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ public:
|
||||
bool on_boundary(const Point &point, double eps) const;
|
||||
// Projection of a point onto the polygon.
|
||||
Point point_projection(const Point &point) const;
|
||||
|
||||
void symmetric_y(const coord_t &y_axis);
|
||||
// Does this expolygon overlap another expolygon?
|
||||
// Either the ExPolygons intersect, or one is fully inside the other,
|
||||
// and it is not inside a hole of the other expolygon.
|
||||
|
||||
+151
-58
@@ -34,7 +34,6 @@ struct SurfaceFillParams
|
||||
coordf_t overlap = 0.;
|
||||
// Angle as provided by the region config, in radians.
|
||||
float angle = 0.f;
|
||||
bool rotate_angle = true;
|
||||
// Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
|
||||
bool bridge;
|
||||
// Non-negative for a bridge.
|
||||
@@ -68,6 +67,9 @@ struct SurfaceFillParams
|
||||
// Params for lattice infill angles
|
||||
float lattice_angle_1 = 0.f;
|
||||
float lattice_angle_2 = 0.f;
|
||||
float infill_lock_depth = 0;
|
||||
float skin_infill_depth = 0;
|
||||
bool symmetric_infill_y_axis = false;
|
||||
|
||||
// Params for 2D honeycomb
|
||||
float infill_overhang_angle = 60.f;
|
||||
@@ -85,7 +87,6 @@ struct SurfaceFillParams
|
||||
RETURN_COMPARE_NON_EQUAL(spacing);
|
||||
RETURN_COMPARE_NON_EQUAL(overlap);
|
||||
RETURN_COMPARE_NON_EQUAL(angle);
|
||||
RETURN_COMPARE_NON_EQUAL(rotate_angle);
|
||||
RETURN_COMPARE_NON_EQUAL(density);
|
||||
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||
@@ -100,33 +101,36 @@ struct SurfaceFillParams
|
||||
RETURN_COMPARE_NON_EQUAL(solid_infill_speed);
|
||||
RETURN_COMPARE_NON_EQUAL(lattice_angle_1);
|
||||
RETURN_COMPARE_NON_EQUAL(lattice_angle_2);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
|
||||
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
|
||||
RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const SurfaceFillParams &rhs) const {
|
||||
return this->extruder == rhs.extruder &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->spacing == rhs.spacing &&
|
||||
this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->rotate_angle == rhs.rotate_angle &&
|
||||
this->bridge == rhs.bridge &&
|
||||
this->bridge_angle == rhs.bridge_angle &&
|
||||
this->density == rhs.density &&
|
||||
// this->dont_adjust == rhs.dont_adjust &&
|
||||
this->anchor_length == rhs.anchor_length &&
|
||||
this->anchor_length_max == rhs.anchor_length_max &&
|
||||
this->flow == rhs.flow &&
|
||||
this->extrusion_role == rhs.extrusion_role &&
|
||||
this->sparse_infill_speed == rhs.sparse_infill_speed &&
|
||||
this->top_surface_speed == rhs.top_surface_speed &&
|
||||
this->solid_infill_speed == rhs.solid_infill_speed &&
|
||||
this->lattice_angle_1 == rhs.lattice_angle_1 &&
|
||||
this->lattice_angle_2 == rhs.lattice_angle_2 &&
|
||||
bool operator==(const SurfaceFillParams &rhs) const {
|
||||
return this->extruder == rhs.extruder &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->spacing == rhs.spacing &&
|
||||
this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->bridge == rhs.bridge &&
|
||||
this->bridge_angle == rhs.bridge_angle &&
|
||||
this->density == rhs.density &&
|
||||
// this->dont_adjust == rhs.dont_adjust &&
|
||||
this->anchor_length == rhs.anchor_length &&
|
||||
this->anchor_length_max == rhs.anchor_length_max &&
|
||||
this->flow == rhs.flow &&
|
||||
this->extrusion_role == rhs.extrusion_role &&
|
||||
this->sparse_infill_speed == rhs.sparse_infill_speed &&
|
||||
this->top_surface_speed == rhs.top_surface_speed &&
|
||||
this->solid_infill_speed == rhs.solid_infill_speed &&
|
||||
this->lattice_angle_1 == rhs.lattice_angle_1 &&
|
||||
this->lattice_angle_2 == rhs.lattice_angle_2 &&
|
||||
this->infill_lock_depth == rhs.infill_lock_depth &&
|
||||
this->skin_infill_depth == rhs.skin_infill_depth &&
|
||||
this->infill_overhang_angle == rhs.infill_overhang_angle;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SurfaceFill {
|
||||
@@ -602,16 +606,34 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_param)
|
||||
{
|
||||
std::vector<SurfaceFill> surface_fills;
|
||||
|
||||
// Fill in a map of a region & surface to SurfaceFillParams.
|
||||
std::set<SurfaceFillParams> set_surface_params;
|
||||
std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
|
||||
SurfaceFillParams params;
|
||||
bool has_internal_voids = false;
|
||||
const PrintObjectConfig& object_config = layer.object()->config();
|
||||
|
||||
auto append_flow_param = [](std::map<Flow, ExPolygons> &flow_params, Flow flow, const ExPolygon &exp) {
|
||||
auto it = flow_params.find(flow);
|
||||
if (it == flow_params.end())
|
||||
flow_params.insert({flow, {exp}});
|
||||
else
|
||||
it->second.push_back(exp);
|
||||
it++;
|
||||
};
|
||||
|
||||
auto append_density_param = [](std::map<float, ExPolygons> &density_params, float density, const ExPolygon &exp) {
|
||||
auto it = density_params.find(density);
|
||||
if (it == density_params.end())
|
||||
density_params.insert({density, {exp}});
|
||||
else
|
||||
it->second.push_back(exp);
|
||||
it++;
|
||||
};
|
||||
|
||||
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
||||
const LayerRegion &layerm = *layer.regions()[region_id];
|
||||
region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
|
||||
@@ -628,26 +650,37 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.lattice_angle_1 = region_config.lattice_angle_1;
|
||||
params.lattice_angle_2 = region_config.lattice_angle_2;
|
||||
params.infill_overhang_angle = region_config.infill_overhang_angle;
|
||||
if (params.pattern == ipLockedZag) {
|
||||
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
|
||||
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
|
||||
}
|
||||
if (params.pattern == ipCrossZag || params.pattern == ipLockedZag) {
|
||||
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
|
||||
} else if (params.pattern == ipZigZag) {
|
||||
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
|
||||
}
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
//FIXME for non-thick bridges, shall we allow a bottom surface pattern?
|
||||
if (surface.is_solid_infill())
|
||||
params.pattern = region_config.internal_solid_infill_pattern.value;
|
||||
else if (surface.is_external() && ! is_bridge) {
|
||||
if(surface.is_top())
|
||||
if (surface.is_solid()) {
|
||||
if (surface.is_external() && !is_bridge) {
|
||||
if (surface.is_top()) {
|
||||
params.pattern = region_config.top_surface_pattern.value;
|
||||
else
|
||||
params.density = float(region_config.top_surface_density);
|
||||
} else { // Surface is bottom
|
||||
params.pattern = region_config.bottom_surface_pattern.value;
|
||||
}
|
||||
else {
|
||||
if(region_config.top_surface_pattern == ipMonotonic || region_config.top_surface_pattern == ipMonotonicLine)
|
||||
params.density = float(region_config.bottom_surface_density);
|
||||
}
|
||||
} else if (surface.is_solid_infill()) {
|
||||
params.pattern = region_config.internal_solid_infill_pattern.value;
|
||||
params.density = 100.f;
|
||||
} else {
|
||||
if (region_config.top_surface_pattern == ipMonotonic || region_config.top_surface_pattern == ipMonotonicLine)
|
||||
params.pattern = ipMonotonic;
|
||||
else
|
||||
params.pattern = ipRectilinear;
|
||||
params.density = 100.f;
|
||||
}
|
||||
} else if (params.density <= 0)
|
||||
continue;
|
||||
} else if (params.density <= 0)
|
||||
continue;
|
||||
|
||||
params.extrusion_role = erInternalInfill;
|
||||
if (is_bridge) {
|
||||
@@ -667,10 +700,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
if (params.extrusion_role == erInternalInfill) {
|
||||
params.angle = float(Geometry::deg2rad(region_config.infill_direction.value));
|
||||
params.rotate_angle = (params.pattern == ipRectilinear || params.pattern == ipLine);
|
||||
} else {
|
||||
params.angle = float(Geometry::deg2rad(region_config.solid_infill_direction.value));
|
||||
params.rotate_angle = region_config.rotate_solid_infill_direction;
|
||||
}
|
||||
|
||||
// Calculate the actual flow we'll be using for this infill.
|
||||
@@ -709,7 +740,28 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
|
||||
}
|
||||
|
||||
auto it_params = set_surface_params.find(params);
|
||||
//get locked region param
|
||||
if (params.pattern == ipLockedZag){
|
||||
const PrintObject *object = layerm.layer()->object();
|
||||
auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1));
|
||||
Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness));
|
||||
//add skin flow
|
||||
append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon);
|
||||
|
||||
Flow skeleton_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skeleton_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness)) ;
|
||||
// add skeleton flow
|
||||
append_flow_param(lock_param.skeleton_flow_params, skeleton_flow, surface.expolygon);
|
||||
|
||||
// add skin density
|
||||
append_density_param(lock_param.skin_density_params, float(0.01 * region_config.skin_infill_density), surface.expolygon);
|
||||
|
||||
// add skin density
|
||||
append_density_param(lock_param.skeleton_density_params, float(0.01 * region_config.skeleton_infill_density), surface.expolygon);
|
||||
|
||||
}
|
||||
|
||||
auto it_params = set_surface_params.find(params);
|
||||
|
||||
if (it_params == set_surface_params.end())
|
||||
it_params = set_surface_params.insert(it_params, params);
|
||||
region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
|
||||
@@ -829,7 +881,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.density = 100.f;
|
||||
params.extrusion_role = erSolidInfill;
|
||||
params.angle = float(Geometry::deg2rad(layerm.region().config().solid_infill_direction.value));
|
||||
params.rotate_angle = layerm.region().config().rotate_solid_infill_direction;
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.flow(frSolidInfill);
|
||||
params.spacing = params.flow.spacing();
|
||||
@@ -914,8 +965,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this);
|
||||
LockRegionParam lock_param;
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this, lock_param);
|
||||
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
||||
const auto resolution = this->object()->print()->config().resolution.value;
|
||||
|
||||
@@ -933,14 +984,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
f->layer_id = this->id();
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->rotate_angle = surface_fill.params.rotate_angle;
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
f->print_config = &this->object()->print()->config();
|
||||
f->print_object_config = &this->object()->config();
|
||||
|
||||
if (surface_fill.params.pattern == ipLightning)
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
if (surface_fill.params.pattern == ipConcentricInternal) {
|
||||
FillConcentricInternal *fill_concentric = dynamic_cast<FillConcentricInternal *>(f.get());
|
||||
assert(fill_concentric != nullptr);
|
||||
fill_concentric->print_config = &this->object()->print()->config();
|
||||
fill_concentric->print_object_config = &this->object()->config();
|
||||
} else if (surface_fill.params.pattern == ipConcentric) {
|
||||
FillConcentric *fill_concentric = dynamic_cast<FillConcentric *>(f.get());
|
||||
assert(fill_concentric != nullptr);
|
||||
fill_concentric->print_config = &this->object()->print()->config();
|
||||
fill_concentric->print_object_config = &this->object()->config();
|
||||
} else if (surface_fill.params.pattern == ipLightning)
|
||||
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
|
||||
double link_max_length = 0.;
|
||||
@@ -970,7 +1029,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.resolution = resolution;
|
||||
params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipConcentricInternal;
|
||||
params.layer_height = layerm->layer()->height;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_2 = surface_fill.params.lattice_angle_2;
|
||||
params.infill_overhang_angle = surface_fill.params.infill_overhang_angle;
|
||||
|
||||
@@ -979,11 +1038,41 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.extrusion_role = surface_fill.params.extrusion_role;
|
||||
params.using_internal_flow = using_internal_flow;
|
||||
params.no_extrusion_overlap = surface_fill.params.overlap;
|
||||
params.config = &layerm->region().config();
|
||||
auto ®ion_config = layerm->region().config();
|
||||
|
||||
ConfigOptionFloats rotate_angles;
|
||||
rotate_angles.deserialize( surface_fill.params.extrusion_role == erInternalInfill ? region_config.sparse_infill_rotate_template.value : region_config.solid_infill_rotate_template.value);
|
||||
auto rotate_angle_idx = f->layer_id % rotate_angles.size();
|
||||
f->rotate_angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]);
|
||||
|
||||
params.config = ®ion_config;
|
||||
params.pattern = surface_fill.params.pattern;
|
||||
if( surface_fill.params.pattern == ipLockedZag ) {
|
||||
params.locked_zag = true;
|
||||
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
|
||||
params.skin_infill_depth = surface_fill.params.skin_infill_depth;
|
||||
f->set_lock_region_param(lock_param);
|
||||
}
|
||||
if (surface_fill.params.pattern == ipCrossZag || surface_fill.params.pattern == ipLockedZag) {
|
||||
if (f->layer_id % 2 == 0) {
|
||||
params.horiz_move -= scale_(region_config.infill_shift_step) * (f->layer_id / 2);
|
||||
} else {
|
||||
params.horiz_move += scale_(region_config.infill_shift_step) * (f->layer_id / 2);
|
||||
}
|
||||
|
||||
params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
|
||||
|
||||
}
|
||||
if (surface_fill.params.pattern == ipGrid)
|
||||
params.can_reverse = false;
|
||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
|
||||
expoly.symmetric_y(params.symmetric_y_axis);
|
||||
}
|
||||
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
f->spacing = surface_fill.params.spacing;
|
||||
surface_fill.surface.expolygon = std::move(expoly);
|
||||
@@ -1023,9 +1112,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
|
||||
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
|
||||
{
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this);
|
||||
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
||||
const auto resolution = this->object()->print()->config().resolution.value;
|
||||
LockRegionParam skin_inner_param;
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this, skin_inner_param);
|
||||
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
||||
const auto resolution = this->object()->print()->config().resolution.value;
|
||||
|
||||
Polylines sparse_infill_polylines{};
|
||||
|
||||
@@ -1059,7 +1149,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
case ipTpmsD:
|
||||
case ipHilbertCurve:
|
||||
case ipArchimedeanChords:
|
||||
case ipOctagramSpiral: break;
|
||||
case ipOctagramSpiral:
|
||||
case ipZigZag:
|
||||
case ipCrossZag:
|
||||
case ipLockedZag: break;
|
||||
}
|
||||
|
||||
// Create the filler object.
|
||||
@@ -1103,8 +1196,8 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
params.resolution = resolution;
|
||||
params.use_arachne = false;
|
||||
params.layer_height = layerm.layer()->height;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_2 = surface_fill.params.lattice_angle_2;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_2 = surface_fill.params.lattice_angle_2;
|
||||
params.infill_overhang_angle = surface_fill.params.infill_overhang_angle;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
|
||||
@@ -15,6 +15,10 @@ public:
|
||||
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
|
||||
~Fill3DHoneycomb() override {}
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
bool use_bridge_flow() const override { return true; }
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
|
||||
@@ -71,6 +71,7 @@ protected:
|
||||
// may not be optimal as the internal infill lines may get extruded before the long infill
|
||||
// lines to which the short infill lines are supposed to anchor.
|
||||
bool no_sort() const override { return false; }
|
||||
bool is_self_crossing() override { return true; }
|
||||
};
|
||||
|
||||
} // namespace FillAdaptive
|
||||
|
||||
@@ -67,6 +67,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
||||
// BBS: for bottom and top surface only
|
||||
// Orca: Replace BBS implementation with Prusa implementation
|
||||
case ipMonotonicLine: return new FillMonotonicLines();
|
||||
case ipZigZag: return new FillZigZag();
|
||||
case ipCrossZag: return new FillCrossZag();
|
||||
case ipLockedZag: return new FillLockedZag();
|
||||
default: throw Slic3r::InvalidArgument("unknown type");
|
||||
}
|
||||
}
|
||||
@@ -243,7 +246,7 @@ void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, Ex
|
||||
|
||||
// Calculate a new spacing to fill width with possibly integer number of lines,
|
||||
// the first and last line being centered at the interval ends.
|
||||
// This function possibly increases the spacing, never decreases,
|
||||
// This function possibly increases the spacing, never decreases,
|
||||
// and for a narrow width the increase in spacing may become severe,
|
||||
// therefore the adjustment is limited to 20% increase.
|
||||
coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
|
||||
@@ -252,8 +255,8 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
|
||||
assert(distance > 0);
|
||||
// floor(width / distance)
|
||||
const auto number_of_intervals = coord_t((width - EPSILON) / distance);
|
||||
coord_t distance_new = (number_of_intervals == 0) ?
|
||||
distance :
|
||||
coord_t distance_new = (number_of_intervals == 0) ?
|
||||
distance :
|
||||
coord_t((width - EPSILON) / number_of_intervals);
|
||||
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
|
||||
assert(factor > 1. - 1e-5);
|
||||
@@ -279,8 +282,8 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
|
||||
|
||||
// Bounding box is the bounding box of a perl object Slic3r::Print::Object (c++ object Slic3r::PrintObject)
|
||||
// The bounding box is only undefined in unit tests.
|
||||
Point out_shift = empty(this->bounding_box) ?
|
||||
surface->expolygon.contour.bounding_box().center() :
|
||||
Point out_shift = empty(this->bounding_box) ?
|
||||
surface->expolygon.contour.bounding_box().center() :
|
||||
this->bounding_box.center();
|
||||
|
||||
#if 0
|
||||
@@ -354,10 +357,10 @@ struct ContourIntersectionPoint {
|
||||
bool could_take_next() const throw() { return ! this->consumed && this->contour_not_taken_length_next > SCALED_EPSILON; }
|
||||
|
||||
// Could extrude a complete segment from this to this->prev_on_contour.
|
||||
bool could_connect_prev() const throw()
|
||||
bool could_connect_prev() const throw()
|
||||
{ return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
|
||||
// Could extrude a complete segment from this to this->next_on_contour.
|
||||
bool could_connect_next() const throw()
|
||||
bool could_connect_next() const throw()
|
||||
{ return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
|
||||
};
|
||||
|
||||
@@ -566,7 +569,7 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, Cont
|
||||
}
|
||||
|
||||
static void take_limited(
|
||||
Polyline &pl1, const Points &contour, const std::vector<double> ¶ms,
|
||||
Polyline &pl1, const Points &contour, const std::vector<double> ¶ms,
|
||||
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
@@ -730,8 +733,8 @@ static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, do
|
||||
// Calculate intersection of a line with a thick segment.
|
||||
// Returns Eucledian parameters of the line / thick segment overlap.
|
||||
static inline bool line_rounded_thick_segment_collision(
|
||||
const Vec2d &line_a, const Vec2d &line_b,
|
||||
const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
|
||||
const Vec2d &line_a, const Vec2d &line_b,
|
||||
const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
|
||||
std::pair<double, double> &out_interval)
|
||||
{
|
||||
const Vec2d line_v0 = line_b - line_a;
|
||||
@@ -794,8 +797,8 @@ static inline bool line_rounded_thick_segment_collision(
|
||||
std::pair<double, double> interval;
|
||||
if (Geometry::liang_barsky_line_clipping_interval(
|
||||
Vec2d(line_p0.dot(dir_x), line_p0.dot(dir_y)),
|
||||
Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
|
||||
BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
|
||||
Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
|
||||
BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
|
||||
interval))
|
||||
extend_interval(interval.first, interval.second);
|
||||
} else
|
||||
@@ -1155,7 +1158,7 @@ void mark_boundary_segments_touching_infill(
|
||||
// Clip the infill polyline by the Eucledian distance along the polyline.
|
||||
SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance);
|
||||
SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance);
|
||||
if (start_point.valid() && end_point.valid() &&
|
||||
if (start_point.valid() && end_point.valid() &&
|
||||
(start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) {
|
||||
// The clipped polyline is non-empty.
|
||||
#ifdef INFILL_DEBUG_OUTPUT
|
||||
@@ -1295,21 +1298,21 @@ struct BoundaryInfillGraph
|
||||
};
|
||||
|
||||
static Direction dir(const Point &p1, const Point &p2) {
|
||||
return p1.x() == p2.x() ?
|
||||
return p1.x() == p2.x() ?
|
||||
(p1.y() < p2.y() ? Up : Down) :
|
||||
(p1.x() < p2.x() ? Right : Left);
|
||||
}
|
||||
|
||||
const Direction dir_prev(const ContourIntersectionPoint &cp) const {
|
||||
assert(cp.prev_on_contour);
|
||||
return cp.could_take_prev() ?
|
||||
return cp.could_take_prev() ?
|
||||
dir(this->point(cp), this->point(*cp.prev_on_contour)) :
|
||||
Taken;
|
||||
}
|
||||
|
||||
const Direction dir_next(const ContourIntersectionPoint &cp) const {
|
||||
assert(cp.next_on_contour);
|
||||
return cp.could_take_next() ?
|
||||
return cp.could_take_next() ?
|
||||
dir(this->point(cp), this->point(*cp.next_on_contour)) :
|
||||
Taken;
|
||||
}
|
||||
@@ -1367,7 +1370,7 @@ static inline void mark_boundary_segments_overlapping_infill(
|
||||
assert(interval.first == 0.);
|
||||
double len_out = closed_contour_distance_ccw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
|
||||
if (len_out < cp.contour_not_taken_length_next) {
|
||||
// Leaving the infill line region before exiting cp.contour_not_taken_length_next,
|
||||
// Leaving the infill line region before exiting cp.contour_not_taken_length_next,
|
||||
// thus at least some of the contour is outside and we will extrude this segment.
|
||||
inside = false;
|
||||
break;
|
||||
@@ -1399,7 +1402,7 @@ static inline void mark_boundary_segments_overlapping_infill(
|
||||
assert(interval.first == 0.);
|
||||
double len_out = closed_contour_distance_cw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
|
||||
if (len_out < cp.contour_not_taken_length_prev) {
|
||||
// Leaving the infill line region before exiting cp.contour_not_taken_length_next,
|
||||
// Leaving the infill line region before exiting cp.contour_not_taken_length_next,
|
||||
// thus at least some of the contour is outside and we will extrude this segment.
|
||||
inside = false;
|
||||
break;
|
||||
@@ -1496,7 +1499,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
|
||||
ContourIntersectionPoint *pthis = &out.map_infill_end_point_to_boundary[it->second];
|
||||
if (pprev) {
|
||||
pprev->next_on_contour = pthis;
|
||||
pthis->prev_on_contour = pprev;
|
||||
pthis->prev_on_contour = pprev;
|
||||
} else
|
||||
pfirst = pthis;
|
||||
contour_intersection_points.emplace_back(pthis);
|
||||
@@ -1521,7 +1524,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
|
||||
ip->param = contour_params[ip->point_idx];
|
||||
// and measure distance to the previous and next intersection point.
|
||||
const double contour_length = contour_params.back();
|
||||
for (ContourIntersectionPoint *ip : contour_intersection_points)
|
||||
for (ContourIntersectionPoint *ip : contour_intersection_points)
|
||||
if (ip->next_on_contour == ip) {
|
||||
assert(ip->prev_on_contour == ip);
|
||||
ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
|
||||
@@ -1556,6 +1559,18 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
|
||||
return out;
|
||||
}
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox Fill::extended_object_bounding_box() const
|
||||
{
|
||||
BoundingBox out = bounding_box;
|
||||
out.merge(Point(out.min.y(), out.min.x()));
|
||||
out.merge(Point(out.max.y(), out.max.x()));
|
||||
|
||||
// The bounding box is scaled by sqrt(2.) to ensure that the bounding box
|
||||
// covers any possible rotations.
|
||||
return out.scaled(sqrt(2.));
|
||||
}
|
||||
|
||||
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
|
||||
{
|
||||
assert(! infill_ordered.empty());
|
||||
@@ -1927,14 +1942,14 @@ static inline void base_support_extend_infill_lines(Polylines &infill, BoundaryI
|
||||
// The contour is supposed to enter the "forbidden" zone outside of the (left, right) band at tbegin and also at tend.
|
||||
static inline void emit_loops_in_band(
|
||||
// Vertical band, which will trim the contour between tbegin and tend.
|
||||
coord_t left,
|
||||
coord_t left,
|
||||
coord_t right,
|
||||
// Contour and its parametrization.
|
||||
const Points &contour,
|
||||
const std::vector<double> &contour_params,
|
||||
// Span of the parameters of an arch to trim with the vertical band.
|
||||
double tbegin,
|
||||
double tend,
|
||||
double tend,
|
||||
// Minimum arch length to put into polylines_out. Shorter arches are not necessary to support a dense support infill.
|
||||
double min_length,
|
||||
Polylines &polylines_out)
|
||||
@@ -1990,13 +2005,13 @@ static inline void emit_loops_in_band(
|
||||
};
|
||||
|
||||
enum InOutBand {
|
||||
Entering,
|
||||
Entering,
|
||||
Leaving,
|
||||
};
|
||||
|
||||
class State {
|
||||
public:
|
||||
State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
|
||||
State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
|
||||
m_left(left), m_right(right), m_min_length(min_length), m_polylines_out(polylines_out) {}
|
||||
|
||||
void add_inner_point(const Point* p)
|
||||
@@ -2297,7 +2312,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
|
||||
#endif // INFILL_DEBUG_OUTPUT
|
||||
|
||||
base_support_extend_infill_lines(infill_ordered, graph, spacing, params);
|
||||
|
||||
|
||||
#ifdef INFILL_DEBUG_OUTPUT
|
||||
export_partial_infill_to_svg(debug_out_path("connect_base_support-extended-%03d.svg", iRun), graph, infill_ordered, polylines_out);
|
||||
#endif // INFILL_DEBUG_OUTPUT
|
||||
@@ -2332,7 +2347,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
|
||||
};
|
||||
|
||||
// Connect infill lines at cp and cpo_next_on_contour.
|
||||
// If the complete arch cannot be taken, then
|
||||
// If the complete arch cannot be taken, then
|
||||
// if (take_first)
|
||||
// take the infill line at cp and an arc from cp towards cp.next_on_contour.
|
||||
// else
|
||||
@@ -2626,7 +2641,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
|
||||
for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
|
||||
const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
|
||||
const SupportArcCost &cost_next = *(&cost_prev + 1);
|
||||
if (cp.contour_not_taken_length_prev > SCALED_EPSILON &&
|
||||
if (cp.contour_not_taken_length_prev > SCALED_EPSILON &&
|
||||
(cost_prev.self_loop ?
|
||||
cost_prev.cost > cap_cost :
|
||||
cost_prev.cost > cost_veryhigh)) {
|
||||
@@ -2643,7 +2658,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
|
||||
polylines_out.emplace_back(std::move(pl));
|
||||
}
|
||||
}
|
||||
if (cp.contour_not_taken_length_next > SCALED_EPSILON &&
|
||||
if (cp.contour_not_taken_length_next > SCALED_EPSILON &&
|
||||
(cost_next.self_loop ?
|
||||
cost_next.cost > cap_cost :
|
||||
cost_next.cost > cost_veryhigh)) {
|
||||
|
||||
@@ -36,6 +36,15 @@ public:
|
||||
InfillFailedException() : Slic3r::RuntimeError("Infill failed") {}
|
||||
};
|
||||
|
||||
struct LockRegionParam
|
||||
{
|
||||
LockRegionParam() {}
|
||||
std::map<float, ExPolygons> skin_density_params;
|
||||
std::map<float, ExPolygons> skeleton_density_params;
|
||||
std::map<Flow, ExPolygons> skin_flow_params;
|
||||
std::map<Flow, ExPolygons> skeleton_flow_params;
|
||||
};
|
||||
|
||||
struct FillParams
|
||||
{
|
||||
bool full_infill() const { return density > 0.9999f; }
|
||||
@@ -72,6 +81,7 @@ struct FillParams
|
||||
// For 2D lattice
|
||||
coordf_t lattice_angle_1 { 0.f };
|
||||
coordf_t lattice_angle_2 { 0.f };
|
||||
InfillPattern pattern{ ipRectilinear };
|
||||
|
||||
// For 2D Honeycomb
|
||||
float infill_overhang_angle { 60 };
|
||||
@@ -85,6 +95,13 @@ struct FillParams
|
||||
const PrintRegionConfig* config{ nullptr };
|
||||
bool dont_sort{ false }; // do not sort the lines, just simply connect them
|
||||
bool can_reverse{true};
|
||||
|
||||
float horiz_move{0.0}; //move infill to get cross zag pattern
|
||||
bool symmetric_infill_y_axis{false};
|
||||
coord_t symmetric_y_axis{0};
|
||||
bool locked_zag{false};
|
||||
float infill_lock_depth{0.0};
|
||||
float skin_infill_depth{0.0};
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||
|
||||
@@ -102,7 +119,7 @@ public:
|
||||
// in radians, ccw, 0 = East
|
||||
float angle;
|
||||
// Orca: enable angle shifting for layer change
|
||||
bool rotate_angle{ true };
|
||||
float rotate_angle{ M_PI/180.0 };
|
||||
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
|
||||
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
|
||||
// If left to zero, the links will not be limited.
|
||||
@@ -135,20 +152,25 @@ public:
|
||||
static bool use_bridge_flow(const InfillPattern type);
|
||||
|
||||
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
|
||||
|
||||
BoundingBox extended_object_bounding_box() const;
|
||||
// Use bridge flow for the fill?
|
||||
virtual bool use_bridge_flow() const { return false; }
|
||||
|
||||
// Do not sort the fill lines to optimize the print head path?
|
||||
virtual bool no_sort() const { return false; }
|
||||
|
||||
virtual bool is_self_crossing() = 0;
|
||||
|
||||
// Return true if infill has a consistent pattern between layers.
|
||||
virtual bool has_consistent_pattern() const { return false; }
|
||||
|
||||
// Perform the fill.
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
|
||||
|
||||
virtual void set_lock_region_param(const LockRegionParam &lock_param){};
|
||||
// BBS: this method is used to fill the ExtrusionEntityCollection.
|
||||
// It call fill_surface by default
|
||||
virtual void fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out);
|
||||
virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out);
|
||||
|
||||
protected:
|
||||
Fill() :
|
||||
@@ -159,7 +181,7 @@ protected:
|
||||
overlap(0.),
|
||||
// Initial angle is undefined.
|
||||
angle(FLT_MAX),
|
||||
rotate_angle(true),
|
||||
rotate_angle(M_PI/180.0),
|
||||
link_max_length(0),
|
||||
loop_clipping(0),
|
||||
// The initial bounding box is empty, therefore undefined.
|
||||
@@ -168,11 +190,11 @@ protected:
|
||||
|
||||
// The expolygon may be modified by the method to avoid a copy.
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams & /* params */,
|
||||
const FillParams & /* params */,
|
||||
unsigned int /* thickness_layers */,
|
||||
const std::pair<float, Point> & /* direction */,
|
||||
const std::pair<float, Point> & /* direction */,
|
||||
ExPolygon /* expolygon */,
|
||||
Polylines & /* polylines_out */) {};
|
||||
Polylines & /* polylines_out */) {}
|
||||
|
||||
// Used for concentric infill to generate ThickPolylines using Arachne.
|
||||
virtual void _fill_surface_single(const FillParams& params,
|
||||
@@ -181,7 +203,7 @@ protected:
|
||||
ExPolygon expolygon,
|
||||
ThickPolylines& thick_polylines_out) {}
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return (rotate_angle && (idx & 1)) ? float(M_PI/2.) : 0; }
|
||||
virtual float _layer_angle(size_t idx) const { return rotate_angle; }
|
||||
|
||||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
~FillConcentric() override = default;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillConcentric(*this); };
|
||||
|
||||
@@ -10,6 +10,7 @@ class FillConcentricInternal : public Fill
|
||||
public:
|
||||
~FillConcentricInternal() override = default;
|
||||
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillConcentricInternal(*this); };
|
||||
|
||||
@@ -14,6 +14,7 @@ class FillCrossHatch : public Fill
|
||||
public:
|
||||
Fill *clone() const override { return new FillCrossHatch(*this); };
|
||||
~FillCrossHatch() override {}
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
|
||||
@@ -168,6 +168,10 @@ void FillGyroid::_fill_surface_single(
|
||||
// align bounding box to a multiple of our grid module
|
||||
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||
|
||||
// Expand the bounding box to avoid artifacts at the edges
|
||||
coord_t expand = 10 * (scale_(this->spacing));
|
||||
bb.offset(expand);
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = make_gyroid_waves(
|
||||
scale_(this->z),
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
// Correction applied to regular infill angle to maximize printing
|
||||
// speed in default configuration (degrees)
|
||||
|
||||
@@ -13,6 +13,7 @@ class FillHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
~FillHoneycomb() override {}
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillHoneycomb(*this); };
|
||||
|
||||
@@ -20,6 +20,7 @@ class Filler : public Slic3r::Fill
|
||||
{
|
||||
public:
|
||||
~Filler() override = default;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
Generator *generator { nullptr };
|
||||
protected:
|
||||
|
||||
@@ -14,6 +14,7 @@ class FillLine : public Fill
|
||||
public:
|
||||
Fill* clone() const override { return new FillLine(*this); };
|
||||
~FillLine() override = default;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
|
||||
@@ -37,6 +37,7 @@ class FillPlanePath : public Fill
|
||||
{
|
||||
public:
|
||||
~FillPlanePath() override = default;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
|
||||
@@ -987,7 +987,6 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
||||
throw;
|
||||
}
|
||||
#endif //INFILL_DEBUG_OUTPUT
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
||||
@@ -1352,8 +1351,11 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
|
||||
return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
|
||||
}
|
||||
|
||||
static void traverse_graph_generate_polylines(
|
||||
const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
|
||||
static void traverse_graph_generate_polylines(const ExPolygonWithOffset &poly_with_offset,
|
||||
const FillParams ¶ms,
|
||||
std::vector<SegmentedIntersectionLine> &segs,
|
||||
const bool consistent_pattern,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// For each outer only chords, measure their maximum distance to the bow of the outer contour.
|
||||
// Mark an outer only chord as consumed, if the distance is low.
|
||||
@@ -1387,34 +1389,28 @@ static void traverse_graph_generate_polylines(
|
||||
pointLast = polylines_out.back().points.back();
|
||||
for (;;) {
|
||||
if (i_intersection == -1) {
|
||||
// The path has been interrupted. Find a next starting point, closest to the previous extruder position.
|
||||
coordf_t dist2min = std::numeric_limits<coordf_t>().max();
|
||||
for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++ i_vline2) {
|
||||
// The path has been interrupted. Find a next starting point.
|
||||
for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++i_vline2) {
|
||||
const SegmentedIntersectionLine &vline = segs[i_vline2];
|
||||
if (! vline.intersections.empty()) {
|
||||
if (!vline.intersections.empty()) {
|
||||
assert(vline.intersections.size() > 1);
|
||||
// Even number of intersections with the loops.
|
||||
assert((vline.intersections.size() & 1) == 0);
|
||||
assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
|
||||
for (int i = 0; i < int(vline.intersections.size()); ++ i) {
|
||||
const SegmentIntersection& intrsctn = vline.intersections[i];
|
||||
|
||||
// For infill that needs to be consistent between layers (like Zig Zag),
|
||||
// we are switching between forward and backward passes based on the line index.
|
||||
const bool forward_pass = !consistent_pattern || (i_vline2 % 2 == 0);
|
||||
for (int i = 0; i < int(vline.intersections.size()); ++i) {
|
||||
const int intrsctn_idx = forward_pass ? i : int(vline.intersections.size()) - i - 1;
|
||||
const SegmentIntersection &intrsctn = vline.intersections[intrsctn_idx];
|
||||
if (intrsctn.is_outer()) {
|
||||
assert(intrsctn.is_low() || i > 0);
|
||||
bool consumed = intrsctn.is_low() ?
|
||||
intrsctn.consumed_vertical_up :
|
||||
vline.intersections[i - 1].consumed_vertical_up;
|
||||
if (! consumed) {
|
||||
coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
|
||||
if (dist2 < dist2min) {
|
||||
dist2min = dist2;
|
||||
i_vline = i_vline2;
|
||||
i_intersection = i;
|
||||
//FIXME We are taking the first left point always. Verify, that the caller chains the paths
|
||||
// by a shortest distance, while reversing the paths if needed.
|
||||
//if (polylines_out.empty())
|
||||
// Initial state, take the first line, which is the first from the left.
|
||||
goto found;
|
||||
}
|
||||
assert(intrsctn.is_low() || intrsctn_idx > 0);
|
||||
const bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : vline.intersections[intrsctn_idx - 1].consumed_vertical_up;
|
||||
if (!consumed) {
|
||||
i_vline = i_vline2;
|
||||
i_intersection = intrsctn_idx;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,9 +1483,13 @@ static void traverse_graph_generate_polylines(
|
||||
// 1) Find possible connection points on the previous / next vertical line.
|
||||
int i_prev = it->left_horizontal();
|
||||
int i_next = it->right_horizontal();
|
||||
bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection);
|
||||
|
||||
// To ensure pattern consistency between layers for Zig Zag infill, we always
|
||||
// try to connect to the next vertical line and never to the previous vertical line.
|
||||
bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection) && !consistent_pattern;
|
||||
bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
|
||||
bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
|
||||
|
||||
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
|
||||
if (i_prev != -1)
|
||||
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
|
||||
@@ -2737,6 +2737,17 @@ static void polylines_from_paths(const std::vector<MonotonicRegionLink> &path, c
|
||||
}
|
||||
}
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox FillRectilinear::extended_object_bounding_box() const {
|
||||
BoundingBox out = this->bounding_box;
|
||||
out.merge(Point(out.min.y(), out.min.x()));
|
||||
out.merge(Point(out.max.y(), out.max.x()));
|
||||
|
||||
// The bounding box is scaled by sqrt(2.) to ensure that the bounding box
|
||||
// covers any possible rotations.
|
||||
return out.scaled(sqrt(2.));
|
||||
}
|
||||
|
||||
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
|
||||
{
|
||||
// At the end, only the new polylines will be rotated back.
|
||||
@@ -2749,6 +2760,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
|
||||
// Rotate polygons so that we can work with vertical lines here
|
||||
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
|
||||
if (params.locked_zag)
|
||||
rotate_vector.first += float(M_PI/2.);
|
||||
rotate_vector.first += angleBase;
|
||||
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
@@ -2766,11 +2779,14 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
return true;
|
||||
}
|
||||
|
||||
BoundingBox bounding_box = poly_with_offset.bounding_box_src();
|
||||
// For infill that needs to be consistent between layers (like Zig Zag),
|
||||
// we use bounding box of whole object to match vertical lines between layers.
|
||||
BoundingBox bounding_box_src = poly_with_offset.bounding_box_src();
|
||||
BoundingBox bounding_box = this->has_consistent_pattern() ? this->extended_object_bounding_box() : bounding_box_src;
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.full_infill() && !params.dont_adjust) {
|
||||
line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
|
||||
line_spacing = this->_adjust_solid_spacing(bounding_box_src.size().x(), line_spacing);
|
||||
this->spacing = unscale<double>(line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
@@ -2792,6 +2808,13 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
if (params.full_infill())
|
||||
x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2;
|
||||
|
||||
int gap_line = params.horiz_move / line_spacing;
|
||||
if (gap_line % 2 == 0) {
|
||||
x0 += params.horiz_move - gap_line * line_spacing;
|
||||
} else {
|
||||
x0 += params.horiz_move - (gap_line - 1) * line_spacing;
|
||||
n_vlines += 1;
|
||||
}
|
||||
#ifdef SLIC3R_DEBUG
|
||||
static int iRun = 0;
|
||||
BoundingBox bbox_svg = poly_with_offset.bounding_box_outer();
|
||||
@@ -2803,7 +2826,6 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
}
|
||||
iRun ++;
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
std::vector<SegmentedIntersectionLine> segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
|
||||
// Connect by horizontal / vertical links, classify the links based on link_max_length as too long.
|
||||
connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length);
|
||||
@@ -2848,8 +2870,9 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
std::vector<MonotonicRegionLink> path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
|
||||
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
|
||||
}
|
||||
} else
|
||||
traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
|
||||
} else {
|
||||
traverse_graph_generate_polylines(poly_with_offset, params, segs, this->has_consistent_pattern(), polylines_out);
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
@@ -2876,6 +2899,11 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
//FIXME rather simplify the paths to avoid very short edges?
|
||||
//assert(! it->has_duplicate_points());
|
||||
it->remove_duplicate_points();
|
||||
|
||||
//get origin direction infill
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
it->symmetric_y(params.symmetric_y_axis);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
@@ -2884,6 +2912,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
assert(! polyline.has_duplicate_points());
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2963,7 +2993,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
|
||||
Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
Polylines polylines_out;
|
||||
if (params.full_infill()) {
|
||||
// Orca Todo: fow now don't use fill_surface_by_multilines for zipzag infill
|
||||
if (params.full_infill() || params.pattern == ipCrossZag || params.pattern == ipZigZag || params.pattern == ipLockedZag) {
|
||||
if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out))
|
||||
BOOST_LOG_TRIVIAL(error) << "FillRectilinear::fill_surface() fill_surface_by_lines() failed to fill a region.";
|
||||
} else {
|
||||
@@ -3409,5 +3440,119 @@ void FillMonotonicLineWGapFill::fill_surface_by_lines(const Surface* surface, co
|
||||
}
|
||||
}*/
|
||||
|
||||
void FillLockedZag::fill_surface_locked_zag (const Surface * surface,
|
||||
const FillParams & params,
|
||||
std::vector<std::pair<Polylines, Flow>> &multi_width_polyline)
|
||||
{
|
||||
// merge different part exps
|
||||
// diff skin flow
|
||||
Polylines skin_lines;
|
||||
Polylines skeloton_lines;
|
||||
double offset_threshold = params.skin_infill_depth;
|
||||
double overlap_threshold = params.infill_lock_depth;
|
||||
Surface cross_surface = *surface;
|
||||
Surface zig_surface = *surface;
|
||||
// inner exps
|
||||
// inner union exps
|
||||
ExPolygons zig_expas = offset_ex({surface->expolygon}, -offset_threshold);
|
||||
ExPolygons cross_expas = diff_ex(surface->expolygon, zig_expas);
|
||||
|
||||
bool zig_get = false;
|
||||
FillParams zig_params = params;
|
||||
zig_params.horiz_move = 0;
|
||||
// generate skeleton for diff density
|
||||
auto generate_for_different_flow = [&multi_width_polyline](const std::map<Flow, ExPolygons> &flow_params, const Polylines &polylines) {
|
||||
auto it = flow_params.begin();
|
||||
while (it != flow_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(it->second);
|
||||
|
||||
Polylines polys = intersection_pl(polylines, region_exp);
|
||||
multi_width_polyline.emplace_back(polys, it->first);
|
||||
it++;
|
||||
}
|
||||
};
|
||||
|
||||
auto it = this->lock_param.skeleton_density_params.begin();
|
||||
while (it != this->lock_param.skeleton_density_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(it->second);
|
||||
ExPolygons exps = intersection_ex(region_exp, zig_expas);
|
||||
zig_params.density = it->first;
|
||||
exps = intersection_ex(offset_ex(exps, overlap_threshold), surface->expolygon);
|
||||
for (ExPolygon &exp : exps) {
|
||||
zig_surface.expolygon = exp;
|
||||
|
||||
Polylines zig_polylines_out = this->fill_surface(&zig_surface, zig_params);
|
||||
skeloton_lines.insert(skeloton_lines.end(), zig_polylines_out.begin(), zig_polylines_out.end());
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
// set skeleton flow
|
||||
generate_for_different_flow(this->lock_param.skeleton_flow_params, skeloton_lines);
|
||||
|
||||
// skin exps
|
||||
bool cross_get = false;
|
||||
FillParams cross_params = params;
|
||||
cross_params.locked_zag = false;
|
||||
auto skin_density = this->lock_param.skin_density_params.begin();
|
||||
while (skin_density != this->lock_param.skin_density_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(skin_density->second);
|
||||
ExPolygons exps = intersection_ex(region_exp, cross_expas);
|
||||
cross_params.density = skin_density->first;
|
||||
for (ExPolygon &exp : exps) {
|
||||
cross_surface.expolygon = exp;
|
||||
Polylines cross_polylines_out = this->fill_surface(&cross_surface, cross_params);
|
||||
skin_lines.insert(skin_lines.end(), cross_polylines_out.begin(), cross_polylines_out.end());
|
||||
}
|
||||
skin_density++;
|
||||
}
|
||||
|
||||
generate_for_different_flow(this->lock_param.skin_flow_params, skin_lines);
|
||||
}
|
||||
|
||||
void FillLockedZag::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out)
|
||||
{
|
||||
Polylines polylines;
|
||||
ThickPolylines thick_polylines;
|
||||
std::vector<std::pair<Polylines, Flow>> multi_width_polyline;
|
||||
try {
|
||||
this->fill_surface_locked_zag(surface, params, multi_width_polyline);
|
||||
}
|
||||
catch (InfillFailedException&) {}
|
||||
|
||||
if (!thick_polylines.empty() || !multi_width_polyline.empty()) {
|
||||
// Save into layer.
|
||||
ExtrusionEntityCollection* eec = nullptr;
|
||||
out.push_back(eec = new ExtrusionEntityCollection());
|
||||
// Only concentric fills are not sorted.
|
||||
eec->no_sort = this->no_sort();
|
||||
size_t idx = eec->entities.size();
|
||||
{
|
||||
for (std::pair<Polylines, Flow> &poly_with_flow: multi_width_polyline) {
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
double flow_mm3_per_mm = poly_with_flow.second.mm3_per_mm();
|
||||
double flow_width = poly_with_flow.second.width();
|
||||
if (params.using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to f->spacing
|
||||
} else {
|
||||
Flow new_flow = poly_with_flow.second.with_spacing(this->spacing);
|
||||
flow_mm3_per_mm = new_flow.mm3_per_mm();
|
||||
flow_width = new_flow.width();
|
||||
}
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(poly_with_flow.first),
|
||||
params.extrusion_role,
|
||||
flow_mm3_per_mm, float(flow_width), poly_with_flow.second.height());
|
||||
}
|
||||
}
|
||||
if (!params.can_reverse) {
|
||||
for (size_t i = idx; i < eec->entities.size(); i++)
|
||||
eec->entities[i]->set_reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
Fill* clone() const override { return new FillRectilinear(*this); }
|
||||
~FillRectilinear() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
// Fill by single directional lines, interconnect the lines along perimeters.
|
||||
@@ -28,6 +29,9 @@ protected:
|
||||
float pattern_shift;
|
||||
};
|
||||
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox extended_object_bounding_box() const;
|
||||
};
|
||||
|
||||
class FillAlignedRectilinear : public FillRectilinear
|
||||
@@ -65,6 +69,7 @@ public:
|
||||
Fill* clone() const override { return new FillGrid(*this); }
|
||||
~FillGrid() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool is_self_crossing() override { return true; }
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
@@ -89,6 +94,7 @@ public:
|
||||
Fill* clone() const override { return new FillTriangles(*this); }
|
||||
~FillTriangles() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool is_self_crossing() override { return true; }
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
@@ -101,6 +107,7 @@ public:
|
||||
Fill* clone() const override { return new FillStars(*this); }
|
||||
~FillStars() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool is_self_crossing() override { return true; }
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
@@ -113,6 +120,7 @@ public:
|
||||
Fill* clone() const override { return new FillCubic(*this); }
|
||||
~FillCubic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool is_self_crossing() override { return true; }
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
@@ -170,6 +178,7 @@ public:
|
||||
public:
|
||||
~FillMonotonicLineWGapFill() override = default;
|
||||
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillMonotonicLineWGapFill(*this); };
|
||||
@@ -179,9 +188,43 @@ private:
|
||||
void fill_surface_by_lines(const Surface* surface, const FillParams& params, Polylines& polylines_out);
|
||||
};*/
|
||||
|
||||
Points sample_grid_pattern(const ExPolygon& expolygon, coord_t spacing, const BoundingBox& global_bounding_box);
|
||||
Points sample_grid_pattern(const ExPolygons& expolygons, coord_t spacing, const BoundingBox& global_bounding_box);
|
||||
Points sample_grid_pattern(const Polygons& polygons, coord_t spacing, const BoundingBox& global_bounding_box);
|
||||
class FillZigZag : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillZigZag(*this); }
|
||||
~FillZigZag() override = default;
|
||||
|
||||
bool has_consistent_pattern() const override { return true; }
|
||||
};
|
||||
|
||||
class FillCrossZag : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill *clone() const override { return new FillCrossZag(*this); }
|
||||
~FillCrossZag() override = default;
|
||||
|
||||
bool has_consistent_pattern() const override { return true; }
|
||||
};
|
||||
|
||||
class FillLockedZag : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill *clone() const override { return new FillLockedZag(*this); }
|
||||
~FillLockedZag() override = default;
|
||||
LockRegionParam lock_param;
|
||||
|
||||
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
|
||||
|
||||
bool has_consistent_pattern() const override { return true; }
|
||||
void set_lock_region_param(const LockRegionParam &lock_param) override { this->lock_param = lock_param;};
|
||||
void fill_surface_locked_zag(const Surface * surface,
|
||||
const FillParams & params,
|
||||
std::vector<std::pair<Polylines, Flow>> &multi_width_polyline);
|
||||
};
|
||||
|
||||
Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing, const BoundingBox &global_bounding_box);
|
||||
Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing, const BoundingBox &global_bounding_box);
|
||||
Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
+131
-417
@@ -1,445 +1,159 @@
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "FillTpmsD.hpp"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <tbb/parallel_for.h>
|
||||
// From Creality Print
|
||||
//#include <cstddef>
|
||||
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Fill/FillBase.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "FillTpmsD.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using namespace std;
|
||||
struct myPoint
|
||||
{
|
||||
coord_t x, y;
|
||||
};
|
||||
class LineSegmentMerger
|
||||
{
|
||||
public:
|
||||
void mergeSegments(const vector<pair<myPoint, myPoint>>& segments, vector<vector<myPoint>>& polylines2)
|
||||
{
|
||||
std::unordered_map<int, myPoint> point_id_xy;
|
||||
std::set<std::pair<int, int>> segment_ids;
|
||||
std::unordered_map<int64_t, int> map_keyxy_pointid;
|
||||
|
||||
auto get_itr = [&](coord_t x, coord_t y) {
|
||||
for (auto i : {0}) //,-2,2
|
||||
{
|
||||
for (auto j : {0}) //,-2,2
|
||||
{
|
||||
int64_t combined_key1 = static_cast<int64_t>(x + i) << 32 | static_cast<uint32_t>(y + j);
|
||||
auto itr1 = map_keyxy_pointid.find(combined_key1);
|
||||
if (itr1 != map_keyxy_pointid.end()) {
|
||||
return itr1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map_keyxy_pointid.end();
|
||||
};
|
||||
|
||||
int pointid = 0;
|
||||
for (const auto& segment : segments) {
|
||||
coord_t x = segment.first.x;
|
||||
coord_t y = segment.first.y;
|
||||
auto itr = get_itr(x, y);
|
||||
int segmentid0 = -1;
|
||||
if (itr == map_keyxy_pointid.end()) {
|
||||
int64_t combined_key = static_cast<int64_t>(x) << 32 | static_cast<uint32_t>(y);
|
||||
segmentid0 = pointid;
|
||||
point_id_xy[pointid] = segment.first;
|
||||
map_keyxy_pointid[combined_key] = pointid++;
|
||||
} else {
|
||||
segmentid0 = itr->second;
|
||||
}
|
||||
int segmentid1 = -1;
|
||||
x = segment.second.x;
|
||||
y = segment.second.y;
|
||||
itr = get_itr(x, y);
|
||||
if (itr == map_keyxy_pointid.end()) {
|
||||
int64_t combined_key = static_cast<int64_t>(x) << 32 | static_cast<uint32_t>(y);
|
||||
segmentid1 = pointid;
|
||||
point_id_xy[pointid] = segment.second;
|
||||
map_keyxy_pointid[combined_key] = pointid++;
|
||||
} else {
|
||||
segmentid1 = itr->second;
|
||||
}
|
||||
|
||||
if (segmentid0 != segmentid1) {
|
||||
segment_ids.insert(segmentid0 < segmentid1 ? std::make_pair(segmentid0, segmentid1) :
|
||||
std::make_pair(segmentid1, segmentid0));
|
||||
}
|
||||
}
|
||||
|
||||
unordered_map<int, vector<int>> graph;
|
||||
unordered_set<int> visited;
|
||||
vector<vector<int>> polylines;
|
||||
|
||||
// Build the graph
|
||||
for (const auto& segment : segment_ids) {
|
||||
graph[segment.first].push_back(segment.second);
|
||||
graph[segment.second].push_back(segment.first);
|
||||
}
|
||||
|
||||
vector<int> startnodes;
|
||||
for (const auto& node : graph) {
|
||||
if (node.second.size() == 1) {
|
||||
startnodes.push_back(node.first);
|
||||
}
|
||||
}
|
||||
|
||||
// Find all connected components
|
||||
for (const auto& point_first : startnodes) {
|
||||
if (visited.find(point_first) == visited.end()) {
|
||||
vector<int> polyline;
|
||||
dfs(point_first, graph, visited, polyline);
|
||||
polylines.push_back(std::move(polyline));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& point : graph) {
|
||||
if (visited.find(point.first) == visited.end()) {
|
||||
vector<int> polyline;
|
||||
dfs(point.first, graph, visited, polyline);
|
||||
polylines.push_back(std::move(polyline));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& pl : polylines) {
|
||||
vector<myPoint> tmpps;
|
||||
for (auto& pid : pl) {
|
||||
tmpps.push_back(point_id_xy[pid]);
|
||||
}
|
||||
polylines2.push_back(tmpps);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void dfs(const int& start_node,
|
||||
std::unordered_map<int, std::vector<int>>& graph,
|
||||
std::unordered_set<int>& visited,
|
||||
std::vector<int>& polyline)
|
||||
{
|
||||
std::vector<int> stack;
|
||||
stack.reserve(graph.size());
|
||||
stack.push_back(start_node);
|
||||
while (!stack.empty()) {
|
||||
int node = stack.back();
|
||||
stack.pop_back();
|
||||
if (!visited.insert(node).second) {
|
||||
continue;
|
||||
}
|
||||
polyline.push_back(node);
|
||||
auto& neighbors = graph[node];
|
||||
for (const auto& neighbor : neighbors) {
|
||||
if (visited.find(neighbor) == visited.end()) {
|
||||
stack.push_back(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace MarchingSquares {
|
||||
struct Point
|
||||
{
|
||||
double x, y;
|
||||
};
|
||||
|
||||
vector<double> getGridValues(int i, int j, vector<vector<double>>& data)
|
||||
{
|
||||
vector<double> values;
|
||||
values.push_back(data[i][j + 1]);
|
||||
values.push_back(data[i + 1][j + 1]);
|
||||
values.push_back(data[i + 1][j]);
|
||||
values.push_back(data[i][j]);
|
||||
return values;
|
||||
}
|
||||
bool needContour(double value, double contourValue) { return value >= contourValue; }
|
||||
Point interpolate(std::vector<std::vector<MarchingSquares::Point>>& posxy,
|
||||
std::vector<int> p1ij,
|
||||
std::vector<int> p2ij,
|
||||
double v1,
|
||||
double v2,
|
||||
double contourValue)
|
||||
{
|
||||
Point p1;
|
||||
p1.x = posxy[p1ij[0]][p1ij[1]].x;
|
||||
p1.y = posxy[p1ij[0]][p1ij[1]].y;
|
||||
Point p2;
|
||||
p2.x = posxy[p2ij[0]][p2ij[1]].x;
|
||||
p2.y = posxy[p2ij[0]][p2ij[1]].y;
|
||||
|
||||
double mu = (contourValue - v1) / (v2 - v1);
|
||||
Point p;
|
||||
p.x = p1.x + mu * (p2.x - p1.x);
|
||||
p.y = p1.y + mu * (p2.y - p1.y);
|
||||
return p;
|
||||
static double scaled_floor(double x,double scale){
|
||||
return std::floor(x/scale)*scale;
|
||||
}
|
||||
|
||||
void process_block(int i,
|
||||
int j,
|
||||
vector<vector<double>>& data,
|
||||
double contourValue,
|
||||
std::vector<std::vector<MarchingSquares::Point>>& posxy,
|
||||
vector<Point>& contourPoints)
|
||||
static Polylines make_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
||||
{
|
||||
vector<double> values = getGridValues(i, j, data);
|
||||
vector<bool> isNeedContour;
|
||||
for (double value : values) {
|
||||
isNeedContour.push_back(needContour(value, contourValue));
|
||||
}
|
||||
int index = 0;
|
||||
if (isNeedContour[0])
|
||||
index |= 1;
|
||||
if (isNeedContour[1])
|
||||
index |= 2;
|
||||
if (isNeedContour[2])
|
||||
index |= 4;
|
||||
if (isNeedContour[3])
|
||||
index |= 8;
|
||||
vector<Point> points;
|
||||
switch (index) {
|
||||
case 0:
|
||||
case 15: break;
|
||||
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
||||
|
||||
case 1:
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
// tolerance in scaled units. clamp the maximum tolerance as there's
|
||||
// no processing-speed benefit to do so beyond a certain point
|
||||
const double tolerance = std::min(line_spacing / 2, FillTpmsD::PatternTolerance) / unscale<double>(scaleFactor);
|
||||
|
||||
break;
|
||||
case 14:
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
break;
|
||||
//scale factor for 5% : 8 712 388
|
||||
// 1z = 10^-6 mm ?
|
||||
const double z = gridZ / scaleFactor;
|
||||
Polylines result;
|
||||
|
||||
case 2:
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
|
||||
break;
|
||||
case 13:
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
break;
|
||||
case 3:
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
|
||||
break;
|
||||
case 12:
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
|
||||
break;
|
||||
case 4:
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
|
||||
break;
|
||||
case 11:
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
break;
|
||||
case 5:
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j}, {i + 1, j}, values[3], values[2], contourValue));
|
||||
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
break;
|
||||
case 6:
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
|
||||
break;
|
||||
case 9:
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
break;
|
||||
case 7:
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
|
||||
break;
|
||||
case 8:
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue));
|
||||
break;
|
||||
case 10:
|
||||
points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue));
|
||||
points.push_back(interpolate(posxy, {i, j}, {i + 1, j}, values[3], values[2], contourValue));
|
||||
|
||||
points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue));
|
||||
points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue));
|
||||
break;
|
||||
}
|
||||
for (Point& p : points) {
|
||||
contourPoints.push_back(p);
|
||||
}
|
||||
//sin(x)*sin(y)*sin(z)-cos(x)*cos(y)*cos(z)=0
|
||||
//2*sin(x)*sin(y)*sin(z)-2*cos(x)*cos(y)*cos(z)=0
|
||||
//(cos(x-y)-cos(x+y))*sin(z)-(cos(x-y)+cos(x+y))*cos(z)=0
|
||||
//(sin(z)-cos(z))*cos(x-y)-(sin(z)+cos(z))*cos(x+y)=0
|
||||
double a=sin(z)-cos(z);
|
||||
double b=sin(z)+cos(z);
|
||||
//a*cos(x-y)-b*cos(x+y)=0
|
||||
//u=x-y, v=x+y
|
||||
double minU=0-height;
|
||||
double maxU=width-0;
|
||||
double minV=0+0;
|
||||
double maxV=width+height;
|
||||
//a*cos(u)-b*cos(v)=0
|
||||
//if abs(b)<abs(a), then u=acos(b/a*cos(v)) is a continuous line
|
||||
//otherwise we swap u and v
|
||||
const bool swapUV=(std::abs(a)>std::abs(b));
|
||||
if(swapUV) {
|
||||
std::swap(a,b);
|
||||
std::swap(minU,minV);
|
||||
std::swap(maxU,maxV);
|
||||
}
|
||||
std::vector<std::pair<double,double>> wave;
|
||||
{//fill one wave
|
||||
const auto v=[&](double u){return acos(a/b*cos(u));};
|
||||
for(int c=0;c<=4;++c){
|
||||
const double u=minU+2*M_PI*c/4;
|
||||
wave.emplace_back(u,v(u));
|
||||
}
|
||||
{//refine
|
||||
int current=0;
|
||||
while(current+1<int(wave.size())){
|
||||
const double u1=wave[current].first;
|
||||
const double u2=wave[current+1].first;
|
||||
const double middleU=(u1+u2)/2;
|
||||
const double v1=wave[current].second;
|
||||
const double v2=wave[current+1].second;
|
||||
const double middleV=v((u1+u2)/2);
|
||||
if(std::abs(middleV-(v1+v2)/2)>tolerance)
|
||||
wave.emplace(wave.begin()+current+1,middleU,middleV);
|
||||
else
|
||||
++current;
|
||||
}
|
||||
}
|
||||
for(int c=1;c<int(wave.size()) && wave.back().first<maxU;++c)//we start from 1 because the 0-th one is already duplicated as the last one in a period
|
||||
wave.emplace_back(wave[c].first+2*M_PI,wave[c].second);
|
||||
}
|
||||
for(double vShift=scaled_floor(minV,2*M_PI);vShift<maxV+2*M_PI;vShift+=2*M_PI) {
|
||||
for(bool forwardRoot:{false,true}) {
|
||||
result.emplace_back();
|
||||
for(const auto &pair:wave) {
|
||||
const double u=pair.first;
|
||||
double v=pair.second;
|
||||
v=(forwardRoot?v:-v)+vShift;
|
||||
const double x=(u+v)/2;
|
||||
const double y=(v-u)/2*(swapUV?-1:1);
|
||||
result.back().points.emplace_back(x*scaleFactor,y*scaleFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
//todo: select the step better
|
||||
return result;
|
||||
}
|
||||
|
||||
void drawContour(double contourValue,
|
||||
int gridSize_w,
|
||||
int gridSize_h,
|
||||
vector<vector<double>>& data,
|
||||
std::vector<std::vector<MarchingSquares::Point>>& posxy,
|
||||
Polylines& repls)
|
||||
// FIXME: needed to fix build on Mac on buildserver
|
||||
constexpr double FillTpmsD::PatternTolerance;
|
||||
|
||||
void FillTpmsD::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
vector<Point> contourPoints;
|
||||
int total_size = (gridSize_h - 1) * (gridSize_w - 1);
|
||||
vector<vector<Point>> contourPointss;
|
||||
contourPointss.resize(total_size);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, total_size),
|
||||
[&contourValue, &posxy, &contourPointss, &data, &gridSize_w](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t k = range.begin(); k < range.end(); ++k) {
|
||||
int i = k / (gridSize_w - 1); //
|
||||
int j = k % (gridSize_w - 1); //
|
||||
process_block(i, j, data, contourValue, posxy, contourPointss[k]);
|
||||
}
|
||||
});
|
||||
|
||||
vector<pair<myPoint, myPoint>> segments2;
|
||||
myPoint p1, p2;
|
||||
for (int k = 0; k < total_size; k++) {
|
||||
for (int i = 0; i < contourPointss[k].size() / 2; i++) {
|
||||
p1.x = scale_(contourPointss[k][i * 2].x);
|
||||
p1.y = scale_(contourPointss[k][i * 2].y);
|
||||
p2.x = scale_(contourPointss[k][i * 2 + 1].x);
|
||||
p2.y = scale_(contourPointss[k][i * 2 + 1].y);
|
||||
segments2.push_back({p1, p2});
|
||||
}
|
||||
}
|
||||
|
||||
LineSegmentMerger merger;
|
||||
vector<vector<myPoint>> result;
|
||||
merger.mergeSegments(segments2, result);
|
||||
|
||||
for (vector<myPoint>& p : result) {
|
||||
Polyline repltmp;
|
||||
for (myPoint& pt : p) {
|
||||
repltmp.points.push_back(Slic3r::Point(pt.x, pt.y));
|
||||
}
|
||||
repltmp.simplify(scale_(0.05f));
|
||||
repls.push_back(repltmp);
|
||||
}
|
||||
}
|
||||
} // namespace MarchingSquares
|
||||
|
||||
static float sin_table[360];
|
||||
static float cos_table[360];
|
||||
static bool g_is_init = false;
|
||||
|
||||
#define PIratio 57.29577951308232 // 180/PI
|
||||
static void initialize_lookup_tables()
|
||||
{
|
||||
for (int i = 0; i < 360; ++i) {
|
||||
float angle = i * (M_PI / 180.0);
|
||||
sin_table[i] = std::sin(angle);
|
||||
cos_table[i] = std::cos(angle);
|
||||
}
|
||||
}
|
||||
|
||||
static float get_sin(float angle)
|
||||
{
|
||||
angle = angle * PIratio;
|
||||
int index = static_cast<int>(std::fmod(angle, 360) + 360) % 360;
|
||||
return sin_table[index];
|
||||
}
|
||||
|
||||
static float get_cos(float angle)
|
||||
{
|
||||
angle = angle * PIratio;
|
||||
int index = static_cast<int>(std::fmod(angle, 360) + 360) % 360;
|
||||
return cos_table[index];
|
||||
}
|
||||
|
||||
FillTpmsD::FillTpmsD()
|
||||
{
|
||||
if (!g_is_init) {
|
||||
initialize_lookup_tables();
|
||||
g_is_init = true;
|
||||
}
|
||||
}
|
||||
void FillTpmsD::_fill_surface_single(const FillParams& params,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point>& direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines& polylines_out)
|
||||
{
|
||||
auto infill_angle = float(this->angle - (CorrectionAngle * 2 * M_PI) / 360.);
|
||||
if (std::abs(infill_angle) >= EPSILON)
|
||||
auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
|
||||
if(std::abs(infill_angle) >= EPSILON)
|
||||
expolygon.rotate(-infill_angle);
|
||||
|
||||
float vari_T = 2.98 * spacing / params.density; // Infill density adjustment factor for TPMS-D
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
// Density adjusted to have a good %of weight.
|
||||
double density_adjusted = std::max(0., params.density * DensityAdjust);
|
||||
// Distance between the gyroid waves in scaled coordinates.
|
||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
auto cenpos = unscale(bb.center());
|
||||
auto boxsize = unscale(bb.size());
|
||||
float xlen = boxsize.x();
|
||||
float ylen = boxsize.y();
|
||||
// align bounding box to a multiple of our grid module
|
||||
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||
|
||||
float delta = 0.25f;
|
||||
float myperiod = 2 * PI / vari_T;
|
||||
float c_z = myperiod * this->z;
|
||||
float cos_z = get_cos(c_z);
|
||||
float sin_z = get_sin(c_z);
|
||||
// generate pattern
|
||||
Polylines polylines = make_waves(
|
||||
scale_(this->z),
|
||||
density_adjusted,
|
||||
this->spacing,
|
||||
ceil(bb.size()(0) / distance) + 1.,
|
||||
ceil(bb.size()(1) / distance) + 1.);
|
||||
|
||||
auto scalar_field = [&](float x, float y) {
|
||||
// TPMS-D
|
||||
float a_x = myperiod * x;
|
||||
float b_y = myperiod * y;
|
||||
float r = get_cos(a_x) * get_cos(b_y) * cos_z - get_sin(a_x) * get_sin(b_y) * sin_z;
|
||||
return r;
|
||||
};
|
||||
// shift the polyline to the grid origin
|
||||
for (Polyline &pl : polylines)
|
||||
pl.translate(bb.min);
|
||||
|
||||
|
||||
std::vector<std::vector<MarchingSquares::Point>> posxy;
|
||||
int i = 0, j = 0;
|
||||
std::vector<MarchingSquares::Point> allptpos;
|
||||
for (float y = -(ylen) / 2.0f - 2; y < (ylen) / 2.0f + 2; y = y + delta, i++) {
|
||||
j = 0;
|
||||
std::vector<MarchingSquares::Point> colposxy;
|
||||
for (float x = -(xlen) / 2.0f - 2; x < (xlen) / 2.0f + 2; x = x + delta, j++) {
|
||||
MarchingSquares::Point pt;
|
||||
pt.x = cenpos.x() + x;
|
||||
pt.y = cenpos.y() + y;
|
||||
colposxy.push_back(pt);
|
||||
}
|
||||
posxy.push_back(colposxy);
|
||||
polylines = intersection_pl(polylines, expolygon);
|
||||
|
||||
if (! polylines.empty()) {
|
||||
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
|
||||
// The infill perimeter lines should be separated by around a single infill line width.
|
||||
const double minlength = scale_(0.8 * this->spacing);
|
||||
polylines.erase(
|
||||
std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
|
||||
polylines.end());
|
||||
}
|
||||
|
||||
std::vector<std::vector<double>> data(posxy.size(), std::vector<double>(posxy[0].size()));
|
||||
|
||||
int width = posxy[0].size();
|
||||
int height = posxy.size();
|
||||
int total_size = (height) * (width);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, total_size),
|
||||
[&width, &scalar_field, &data, &posxy](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t k = range.begin(); k < range.end(); ++k) {
|
||||
int i = k / (width);
|
||||
int j = k % (width);
|
||||
data[i][j] = scalar_field(posxy[i][j].x, posxy[i][j].y);
|
||||
}
|
||||
});
|
||||
|
||||
Polylines polylines;
|
||||
MarchingSquares::drawContour(0, j, i, data, posxy, polylines);
|
||||
|
||||
polylines = intersection_pl(polylines, expolygon);
|
||||
|
||||
if (!polylines.empty()) {
|
||||
// connect lines
|
||||
size_t polylines_out_first_idx = polylines_out.size();
|
||||
if (params.dont_connect())
|
||||
append(polylines_out, chain_polylines(polylines));
|
||||
if (! polylines.empty()) {
|
||||
// connect lines
|
||||
size_t polylines_out_first_idx = polylines_out.size();
|
||||
if (params.dont_connect())
|
||||
append(polylines_out, chain_polylines(polylines));
|
||||
else
|
||||
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
||||
// new paths must be rotated back
|
||||
|
||||
// new paths must be rotated back
|
||||
if (std::abs(infill_angle) >= EPSILON) {
|
||||
for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++it)
|
||||
it->rotate(infill_angle);
|
||||
}
|
||||
for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
|
||||
it->rotate(infill_angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
#ifndef slic3r_FillTpmsD_hpp_
|
||||
#define slic3r_FillTpmsD_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include <utility>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "FillBase.hpp"
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/Polyline.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
class Point;
|
||||
|
||||
class FillTpmsD : public Fill
|
||||
{
|
||||
public:
|
||||
FillTpmsD();
|
||||
FillTpmsD() {}
|
||||
Fill* clone() const override { return new FillTpmsD(*this); }
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
|
||||
|
||||
// Correction applied to regular infill angle to maximize printing
|
||||
// speed in default configuration (degrees)
|
||||
@@ -24,8 +30,17 @@ public:
|
||||
const std::pair<float, Point>& direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines& polylines_out) override;
|
||||
|
||||
bool is_self_crossing() override { return false; }
|
||||
|
||||
// Density adjustment to have a good %of weight.
|
||||
static constexpr double DensityAdjust = 2.1;
|
||||
|
||||
// Gyroid upper resolution tolerance (mm^-2)
|
||||
static constexpr double PatternTolerance = 0.1;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillTpmsD_hpp_
|
||||
#endif // slic3r_FillTpmsD_hpp_
|
||||
@@ -82,6 +82,13 @@ public:
|
||||
|
||||
bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; }
|
||||
|
||||
bool operator!=(const Flow &rhs) const{
|
||||
return m_width != rhs.m_width || m_height != rhs.m_height || m_nozzle_diameter != rhs.m_nozzle_diameter || m_bridge != rhs.m_bridge;
|
||||
}
|
||||
|
||||
bool operator <(const Flow &rhs) const {
|
||||
return this->mm3_per_mm() < rhs.mm3_per_mm();
|
||||
}
|
||||
Flow with_width (float width) const {
|
||||
assert(! m_bridge);
|
||||
return Flow(width, m_height, rounded_rectangle_extrusion_spacing(width, m_height), m_nozzle_diameter, m_bridge);
|
||||
|
||||
@@ -507,4 +507,11 @@ BoundingBox get_extents_rotated(const MultiPoint &mp, double angle)
|
||||
return get_extents_rotated(mp.points, angle);
|
||||
}
|
||||
|
||||
void MultiPoint::symmetric_y(const coord_t &x_axis)
|
||||
{
|
||||
for (Point &pt : points) {
|
||||
pt(0) = 2 * x_axis - pt(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
bool first_intersection(const Line& line, Point* intersection) const;
|
||||
bool intersections(const Line &line, Points *intersections) const;
|
||||
|
||||
void symmetric_y(const coord_t &y_axis);
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
static Points visivalingam(const Points& pts, const double tolerance);
|
||||
static Points concave_hull_2d(const Points& pts, const double tolerence);
|
||||
|
||||
@@ -783,10 +783,10 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
||||
|
||||
static std::vector<std::string> s_Preset_print_options {
|
||||
"layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode",
|
||||
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
||||
"top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "bottom_shell_layers", "bottom_shell_thickness",
|
||||
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction",
|
||||
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern",
|
||||
"infill_direction", "solid_infill_direction", "rotate_solid_infill_direction", "counterbore_hole_bridging",
|
||||
"infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density",
|
||||
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
|
||||
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset",
|
||||
"support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing",
|
||||
@@ -806,8 +806,9 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"filename_format", "wall_filament", "support_bottom_z_distance",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width",
|
||||
"inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width",
|
||||
"outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
|
||||
"skin_infill_line_width","skeleton_infill_line_width",
|
||||
"top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow",
|
||||
"elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
|
||||
"prime_tower_width", "prime_tower_brim_width", "prime_volume",
|
||||
|
||||
@@ -1387,7 +1387,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (!validate_extrusion_width(object->config(), "support_line_width", layer_height, err_msg))
|
||||
return {err_msg, object, "support_line_width"};
|
||||
}
|
||||
for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width" })
|
||||
for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width","skin_infill_line_width" ,"skeleton_infill_line_width"})
|
||||
for (const PrintRegion ®ion : object->all_regions())
|
||||
if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
|
||||
return {err_msg, object, opt_key};
|
||||
|
||||
+366
-233
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@ enum AuthorizationType {
|
||||
enum InfillPattern : int {
|
||||
ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipTpmsD, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip2DHoneycomb, ip3DHoneycomb,
|
||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
|
||||
ipLightning, ipCrossHatch, ipQuarterCubic,
|
||||
ipLightning, ipCrossHatch, ipQuarterCubic, ipZigZag, ipCrossZag, ipLockedZag,
|
||||
ipCount,
|
||||
};
|
||||
|
||||
@@ -952,6 +952,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, bridge_speed))
|
||||
((ConfigOptionFloatOrPercent, internal_bridge_speed))
|
||||
((ConfigOptionEnum<EnsureVerticalShellThickness>, ensure_vertical_shell_thickness))
|
||||
((ConfigOptionPercent, top_surface_density))
|
||||
((ConfigOptionPercent, bottom_surface_density))
|
||||
((ConfigOptionEnum<InfillPattern>, top_surface_pattern))
|
||||
((ConfigOptionEnum<InfillPattern>, bottom_surface_pattern))
|
||||
((ConfigOptionEnum<InfillPattern>, internal_solid_infill_pattern))
|
||||
@@ -959,7 +961,10 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, outer_wall_speed))
|
||||
((ConfigOptionFloat, infill_direction))
|
||||
((ConfigOptionFloat, solid_infill_direction))
|
||||
((ConfigOptionBool, rotate_solid_infill_direction))
|
||||
((ConfigOptionString, solid_infill_rotate_template))
|
||||
((ConfigOptionBool, symmetric_infill_y_axis))
|
||||
((ConfigOptionFloat, infill_shift_step))
|
||||
((ConfigOptionString, sparse_infill_rotate_template))
|
||||
((ConfigOptionPercent, sparse_infill_density))
|
||||
((ConfigOptionEnum<InfillPattern>, sparse_infill_pattern))
|
||||
((ConfigOptionFloat, lattice_angle_1))
|
||||
@@ -979,7 +984,12 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionPercent, infill_wall_overlap))
|
||||
((ConfigOptionPercent, top_bottom_infill_wall_overlap))
|
||||
((ConfigOptionFloat, sparse_infill_speed))
|
||||
//BBS
|
||||
((ConfigOptionPercent, skeleton_infill_density))
|
||||
((ConfigOptionPercent, skin_infill_density))
|
||||
((ConfigOptionFloat, infill_lock_depth))
|
||||
((ConfigOptionFloat, skin_infill_depth))
|
||||
((ConfigOptionFloatOrPercent, skin_infill_line_width))
|
||||
((ConfigOptionFloatOrPercent, skeleton_infill_line_width))
|
||||
((ConfigOptionBool, infill_combination))
|
||||
// Orca:
|
||||
((ConfigOptionFloatOrPercent, infill_combination_max_layer_height))
|
||||
|
||||
@@ -1073,9 +1073,10 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "sparse_infill_filament"
|
||||
|| opt_key == "solid_infill_filament"
|
||||
|| opt_key == "sparse_infill_line_width"
|
||||
|| opt_key == "skin_infill_line_width"
|
||||
|| opt_key == "skeleton_infill_line_width"
|
||||
|| opt_key == "infill_direction"
|
||||
|| opt_key == "solid_infill_direction"
|
||||
|| opt_key == "rotate_solid_infill_direction"
|
||||
|| opt_key == "ensure_vertical_shell_thickness"
|
||||
|| opt_key == "bridge_angle"
|
||||
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
|
||||
@@ -1091,13 +1092,23 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "infill_anchor"
|
||||
|| opt_key == "infill_anchor_max"
|
||||
|| opt_key == "top_surface_line_width"
|
||||
|| opt_key == "top_surface_density"
|
||||
|| opt_key == "bottom_surface_density"
|
||||
|| opt_key == "initial_layer_line_width"
|
||||
|| opt_key == "small_area_infill_flow_compensation"
|
||||
|| opt_key == "lattice_angle_1"
|
||||
|| opt_key == "lattice_angle_2"
|
||||
|| opt_key == "infill_overhang_angle") {
|
||||
steps.emplace_back(posInfill);
|
||||
} else if (opt_key == "sparse_infill_pattern") {
|
||||
} else if (opt_key == "sparse_infill_pattern"
|
||||
|| opt_key == "symmetric_infill_y_axis"
|
||||
|| opt_key == "infill_shift_step"
|
||||
|| opt_key == "sparse_infill_rotate_template"
|
||||
|| opt_key == "solid_infill_rotate_template"
|
||||
|| opt_key == "skeleton_infill_density"
|
||||
|| opt_key == "skin_infill_density"
|
||||
|| opt_key == "infill_lock_depth"
|
||||
|| opt_key == "skin_infill_depth") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
} else if (opt_key == "sparse_infill_density") {
|
||||
// One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes),
|
||||
|
||||
@@ -665,8 +665,8 @@ if (SLIC3R_STATIC)
|
||||
target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0)
|
||||
endif()
|
||||
|
||||
if (HAVE_SPNAV)
|
||||
target_link_libraries(libslic3r_gui spnav)
|
||||
if (SPNAV_LIB)
|
||||
target_link_libraries(libslic3r_gui ${SPNAV_LIB})
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL AND UNIX AND NOT APPLE)
|
||||
|
||||
@@ -49,7 +49,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para
|
||||
def.min = 0;
|
||||
def.max = 214700;
|
||||
def.width = 10; // increase width for large scale printers with 4 digit values
|
||||
def.sidetext = L("mm");
|
||||
def.sidetext = "mm"; // milimeters, don't need translation
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
key = "rect_size";
|
||||
@@ -60,7 +60,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para
|
||||
def.min = -107350;
|
||||
def.max = 107350;
|
||||
def.width = 10; // increase width for large scale printers with 4 digit values
|
||||
def.sidetext = L("mm");
|
||||
def.sidetext = "mm"; // milimeters, don't need translation
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
key = "rect_origin";
|
||||
@@ -69,7 +69,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.width = 10; // match size
|
||||
def.sidetext = L("mm");
|
||||
def.sidetext = "mm"; // milimeters, don't need translation
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
key = "diameter";
|
||||
|
||||
@@ -313,7 +313,7 @@ void CaliPresetCustomRangePanel::create_panel(wxWindow* parent)
|
||||
m_title_texts[i]->Wrap(-1);
|
||||
m_title_texts[i]->SetFont(::Label::Body_14);
|
||||
item_sizer->Add(m_title_texts[i], 0, wxALL, 0);
|
||||
m_value_inputs[i] = new TextInput(parent, wxEmptyString, wxString::FromUTF8("°C"), "", wxDefaultPosition, CALIBRATION_FROM_TO_INPUT_SIZE, 0);
|
||||
m_value_inputs[i] = new TextInput(parent, wxEmptyString, wxString::FromUTF8("\u2103" /* °C */), "", wxDefaultPosition, CALIBRATION_FROM_TO_INPUT_SIZE, 0);
|
||||
m_value_inputs[i]->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
m_value_inputs[i]->GetTextCtrl()->Bind(wxEVT_TEXT, [this, i](wxCommandEvent& event) {
|
||||
std::string number = m_value_inputs[i]->GetTextCtrl()->GetValue().ToStdString();
|
||||
@@ -392,7 +392,7 @@ void CaliPresetTipsPanel::create_panel(wxWindow* parent)
|
||||
auto nozzle_temp_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto nozzle_temp_text = new Label(parent, _L("Nozzle temperature"));
|
||||
nozzle_temp_text->SetFont(Label::Body_12);
|
||||
m_nozzle_temp = new TextInput(parent, wxEmptyString, wxString::FromUTF8("°C"), "", wxDefaultPosition, CALIBRATION_FROM_TO_INPUT_SIZE, wxTE_READONLY);
|
||||
m_nozzle_temp = new TextInput(parent, wxEmptyString, wxString::FromUTF8("\u2103" /* °C */), "", wxDefaultPosition, CALIBRATION_FROM_TO_INPUT_SIZE, wxTE_READONLY);
|
||||
m_nozzle_temp->SetBorderWidth(0);
|
||||
nozzle_temp_sizer->Add(nozzle_temp_text, 0, wxALIGN_LEFT);
|
||||
nozzle_temp_sizer->Add(m_nozzle_temp, 0, wxEXPAND);
|
||||
@@ -439,7 +439,7 @@ void CaliPresetTipsPanel::set_params(int nozzle_temp, int bed_temp, float max_vo
|
||||
m_nozzle_temp->GetTextCtrl()->SetValue(text_nozzle_temp);
|
||||
|
||||
std::string bed_temp_text = bed_temp==0 ? "-": std::to_string(bed_temp);
|
||||
m_bed_temp->SetLabel(wxString::FromUTF8(bed_temp_text + "°C"));
|
||||
m_bed_temp->SetLabel(wxString::FromUTF8(bed_temp_text + "\u2103" /* °C */));
|
||||
|
||||
wxString flow_val_text = wxString::Format("%0.2f", max_volumetric);
|
||||
m_max_volumetric_speed->GetTextCtrl()->SetValue(flow_val_text);
|
||||
|
||||
@@ -472,6 +472,20 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
// layer_height shouldn't be equal to zero
|
||||
float skin_depth = config->opt_float("skin_infill_depth");
|
||||
if (config->opt_float("infill_lock_depth") > skin_depth) {
|
||||
const wxString msg_text = _(L("lock depth should smaller than skin depth.\nReset to 50% of skin depth"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("infill_lock_depth", new ConfigOptionFloat(skin_depth / 2));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector<std::string> const &keys, std::map<ObjectBase *, ModelConfig *> const &configs)
|
||||
@@ -526,7 +540,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
|
||||
// sparse_infill_filament uses the same logic as in Print::extruders()
|
||||
for (auto el : { "sparse_infill_pattern", "infill_combination",
|
||||
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"})
|
||||
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
|
||||
toggle_line(el, have_infill);
|
||||
|
||||
bool have_combined_infill = config->opt_bool("infill_combination") && have_infill;
|
||||
@@ -539,6 +553,19 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
bool has_infill_anchors = have_infill && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0 && infill_anchor;
|
||||
toggle_field("infill_anchor", has_infill_anchors);
|
||||
|
||||
//cross zag
|
||||
bool is_cross_zag = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value == InfillPattern::ipCrossZag;
|
||||
bool is_locked_zig = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value == InfillPattern::ipLockedZag;
|
||||
|
||||
toggle_line("infill_shift_step", is_cross_zag || is_locked_zig);
|
||||
|
||||
for (auto el : { "skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth","skin_infill_line_width", "skeleton_infill_line_width" })
|
||||
toggle_line(el, is_locked_zig);
|
||||
|
||||
bool is_zig_zag = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value == InfillPattern::ipZigZag;
|
||||
|
||||
toggle_line("symmetric_infill_y_axis", is_zig_zag || is_cross_zag || is_locked_zig);
|
||||
|
||||
bool has_spiral_vase = config->opt_bool("spiral_mode");
|
||||
toggle_line("spiral_mode_smooth", has_spiral_vase);
|
||||
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
|
||||
@@ -552,7 +579,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
for (auto el : { "infill_direction", "sparse_infill_line_width",
|
||||
"sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle", "internal_bridge_angle",
|
||||
"solid_infill_direction", "rotate_solid_infill_direction", "internal_solid_infill_pattern", "solid_infill_filament",
|
||||
"solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "solid_infill_filament",
|
||||
})
|
||||
toggle_field(el, have_infill || has_solid_infill);
|
||||
|
||||
@@ -823,6 +850,22 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
for (auto el : { "lattice_angle_1", "lattice_angle_2"})
|
||||
toggle_line(el, lattice_options);
|
||||
|
||||
//Orca: hide rotate template for solid infill if not support
|
||||
const auto _sparse_infill_pattern = config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value;
|
||||
bool show_sparse_infill_rotate_template = _sparse_infill_pattern == ipRectilinear || _sparse_infill_pattern == ipLine ||
|
||||
_sparse_infill_pattern == ipZigZag || _sparse_infill_pattern == ipCrossZag ||
|
||||
_sparse_infill_pattern == ipLockedZag;
|
||||
|
||||
toggle_line("sparse_infill_rotate_template", show_sparse_infill_rotate_template);
|
||||
|
||||
//Orca: hide rotate template for solid infill if not support
|
||||
const auto _solid_infill_pattern = config->option<ConfigOptionEnum<InfillPattern>>("internal_solid_infill_pattern")->value;
|
||||
bool show_solid_infill_rotate_template = _solid_infill_pattern == ipRectilinear || _solid_infill_pattern == ipMonotonic ||
|
||||
_solid_infill_pattern == ipMonotonicLine || _solid_infill_pattern == ipAlignedRectilinear;
|
||||
|
||||
toggle_line("solid_infill_rotate_template", show_solid_infill_rotate_template);
|
||||
|
||||
|
||||
toggle_line("infill_overhang_angle", config->opt_enum<InfillPattern>("sparse_infill_pattern") == InfillPattern::ip2DHoneycomb);
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
||||
const auto &variant = model.variants[i];
|
||||
|
||||
const auto label = model.technology == ptFFF
|
||||
? from_u8((boost::format("%1% %2% %3%") % variant.name % _utf8(L("mm")) % _utf8(L("nozzle"))).str())
|
||||
? from_u8((boost::format("%1% %2% %3%") % variant.name % _utf8("mm") % _utf8(L("nozzle"))).str())
|
||||
: from_u8(model.name);
|
||||
|
||||
if (i == 1) {
|
||||
@@ -1353,7 +1353,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||
|
||||
auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_nozzle = new wxStaticText(this, wxID_ANY, _L("Nozzle Diameter:"));
|
||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, "mm");
|
||||
sizer_nozzle->AddGrowableCol(0, 1);
|
||||
sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_nozzle->Add(diam_nozzle);
|
||||
@@ -1367,7 +1367,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||
|
||||
auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_filam = new wxStaticText(this, wxID_ANY, _L("Filament Diameter:"));
|
||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, "mm");
|
||||
sizer_filam->AddGrowableCol(0, 1);
|
||||
sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_filam->Add(diam_filam);
|
||||
@@ -1448,7 +1448,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
|
||||
auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_extr = new wxStaticText(this, wxID_ANY, _L("Extrusion Temperature:"));
|
||||
auto *unit_extr = new wxStaticText(this, wxID_ANY, "°C");
|
||||
auto *unit_extr = new wxStaticText(this, wxID_ANY, "\u2103" /* °C */);
|
||||
sizer_extr->AddGrowableCol(0, 1);
|
||||
sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_extr->Add(spin_extr);
|
||||
@@ -1462,7 +1462,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
|
||||
auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature:"));
|
||||
auto *unit_bed = new wxStaticText(this, wxID_ANY, "°C");
|
||||
auto *unit_bed = new wxStaticText(this, wxID_ANY, "\u2103" /* °C */);
|
||||
sizer_bed->AddGrowableCol(0, 1);
|
||||
sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_bed->Add(spin_bed);
|
||||
|
||||
@@ -1870,7 +1870,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_bed_size_item(wxWindow *parent)
|
||||
// ORCA use icon on input box to match style with other Point fields
|
||||
horizontal_sizer->Add(length_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
wxBoxSizer *length_input_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_bed_size_x_input = new TextInput(parent, "200", _L("mm"), "inputbox_x", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_size_x_input = new TextInput(parent, "200", "mm", "inputbox_x", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
wxTextValidator validator(wxFILTER_DIGITS);
|
||||
m_bed_size_x_input->GetTextCtrl()->SetValidator(validator);
|
||||
length_input_sizer->Add(m_bed_size_x_input, 0, wxEXPAND | wxLEFT, FromDIP(5));
|
||||
@@ -1880,7 +1880,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_bed_size_item(wxWindow *parent)
|
||||
// ORCA use icon on input box to match style with other Point fields
|
||||
horizontal_sizer->Add(width_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
wxBoxSizer *width_input_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_bed_size_y_input = new TextInput(parent, "200", _L("mm"), "inputbox_y", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_size_y_input = new TextInput(parent, "200", "mm", "inputbox_y", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_size_y_input->GetTextCtrl()->SetValidator(validator);
|
||||
width_input_sizer->Add(m_bed_size_y_input, 0, wxEXPAND | wxALL, 0);
|
||||
horizontal_sizer->Add(width_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
|
||||
@@ -1903,7 +1903,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_origin_item(wxWindow *parent)
|
||||
// ORCA use icon on input box to match style with other Point fields
|
||||
horizontal_sizer->Add(length_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
wxBoxSizer *length_input_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_bed_origin_x_input = new TextInput(parent, "0", _L("mm"), "inputbox_x", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_origin_x_input = new TextInput(parent, "0", "mm", "inputbox_x", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
wxTextValidator validator(wxFILTER_DIGITS);
|
||||
m_bed_origin_x_input->GetTextCtrl()->SetValidator(validator);
|
||||
length_input_sizer->Add(m_bed_origin_x_input, 0, wxEXPAND | wxLEFT, FromDIP(5)); // Align with other
|
||||
@@ -1913,7 +1913,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_origin_item(wxWindow *parent)
|
||||
// ORCA use icon on input box to match style with other Point fields
|
||||
horizontal_sizer->Add(width_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
wxBoxSizer *width_input_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_bed_origin_y_input = new TextInput(parent, "0", _L("mm"), "inputbox_y", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_origin_y_input = new TextInput(parent, "0", "mm", "inputbox_y", wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER);
|
||||
m_bed_origin_y_input->GetTextCtrl()->SetValidator(validator);
|
||||
width_input_sizer->Add(m_bed_origin_y_input, 0, wxEXPAND | wxALL, 0);
|
||||
horizontal_sizer->Add(width_input_sizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
|
||||
@@ -2006,7 +2006,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_max_print_height_item(wxWindow *pa
|
||||
horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
|
||||
wxBoxSizer *hight_input_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_print_height_input = new TextInput(parent, "200", _L("mm"), wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER); // Use same alignment with all other input boxes
|
||||
m_print_height_input = new TextInput(parent, "200", "mm", wxEmptyString, wxDefaultPosition, PRINTER_SPACE_SIZE, wxTE_PROCESS_ENTER); // Use same alignment with all other input boxes
|
||||
wxTextValidator validator(wxFILTER_DIGITS);
|
||||
m_print_height_input->GetTextCtrl()->SetValidator(validator);
|
||||
hight_input_sizer->Add(m_print_height_input, 0, wxEXPAND | wxLEFT, FromDIP(5));
|
||||
|
||||
@@ -3316,7 +3316,7 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
|
||||
if (jj.contains("errno")) {
|
||||
if (jj["errno"].is_number()) {
|
||||
if (jj["errno"].get<int>() == -2) {
|
||||
wxString text = _L("The current chamber temperature or the target chamber temperature exceeds 45\u2103. "
|
||||
wxString text = _L("The current chamber temperature or the target chamber temperature exceeds 45\u2103. " /* 45°C */
|
||||
"In order to avoid extruder clogging, low temperature filament (PLA/PETG/TPU) is not allowed to be loaded.");
|
||||
GUI::wxGetApp().push_notification(text);
|
||||
}
|
||||
@@ -3330,11 +3330,11 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
|
||||
wxString text;
|
||||
if (jj["errno"].get<int>() == -2) {
|
||||
text = _L("Low temperature filament (PLA/PETG/TPU) is loaded in the extruder. "
|
||||
"In order to avoid extruder clogging, it is not allowed to set the chamber temperature above 45\u2103.");
|
||||
"In order to avoid extruder clogging, it is not allowed to set the chamber temperature above 45\u2103." /* 45°C */);
|
||||
}
|
||||
else if (jj["errno"].get<int>() == -4) {
|
||||
text = _L("When you set the chamber temperature below 40\u2103, the chamber temperature control will not be activated, "
|
||||
"and the target chamber temperature will automatically be set to 0\u2103.");
|
||||
"and the target chamber temperature will automatically be set to 0\u2103." /* 0°C */);
|
||||
}
|
||||
if(!text.empty()){
|
||||
#if __WXOSX__
|
||||
|
||||
@@ -127,21 +127,21 @@ void ExtrusionCalibration::create()
|
||||
wxWindow::GetTextExtent(_L("Bed Temperature")).x),
|
||||
wxWindow::GetTextExtent(_L("Max volumetric speed")).x),
|
||||
EXTRUSION_CALIBRATION_INPUT_SIZE.x);
|
||||
m_nozzle_temp = new TextInput(m_step_1_panel, wxEmptyString, _L("\u2103"), "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
m_nozzle_temp = new TextInput(m_step_1_panel, wxEmptyString, "\u2103" /* °C */, "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
nozzle_temp_sizer->Add(nozzle_temp_text, 0, wxALIGN_LEFT);
|
||||
nozzle_temp_sizer->AddSpacer(FromDIP(4));
|
||||
nozzle_temp_sizer->Add(m_nozzle_temp, 0, wxEXPAND);
|
||||
|
||||
auto bed_temp_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto bed_temp_text = new wxStaticText(m_step_1_panel, wxID_ANY, _L("Bed temperature"));
|
||||
m_bed_temp = new TextInput(m_step_1_panel, wxEmptyString, _L("\u2103"), "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
m_bed_temp = new TextInput(m_step_1_panel, wxEmptyString, "\u2103" /* °C */, "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
bed_temp_sizer->Add(bed_temp_text, 0, wxALIGN_LEFT);
|
||||
bed_temp_sizer->AddSpacer(FromDIP(4));
|
||||
bed_temp_sizer->Add(m_bed_temp, 0, wxEXPAND);
|
||||
|
||||
auto max_flow_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto max_flow_text = new wxStaticText(m_step_1_panel, wxID_ANY, _L("Max volumetric speed"));
|
||||
m_max_flow_ratio = new TextInput(m_step_1_panel, wxEmptyString, _L("mm\u00B3"), "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
m_max_flow_ratio = new TextInput(m_step_1_panel, wxEmptyString, "mm³", "", wxDefaultPosition, { max_input_width, EXTRUSION_CALIBRATION_INPUT_SIZE.y }, wxTE_READONLY);
|
||||
max_flow_sizer->Add(max_flow_text, 0, wxALIGN_LEFT);
|
||||
max_flow_sizer->AddSpacer(FromDIP(4));
|
||||
max_flow_sizer->Add(m_max_flow_ratio, 0, wxEXPAND);
|
||||
|
||||
@@ -430,6 +430,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
||||
str = str_out;
|
||||
set_value(str, true);
|
||||
}
|
||||
} else if (m_opt.opt_key == "sparse_infill_rotate_template" || m_opt.opt_key == "solid_infill_rotate_template") {
|
||||
if (!ConfigOptionFloats::validate_string(str.utf8_string())) {
|
||||
show_error(m_parent, format_wxstr(_L("This parameter expects a comma-delimited list of numbers. E.g, \"0,90\".")));
|
||||
wxString old_value(boost::any_cast<std::string>(m_value));
|
||||
this->set_value(old_value, true); // Revert to previous value
|
||||
} else {
|
||||
// Valid string, so update m_value with the new string from the control.
|
||||
m_value = into_u8(str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_value = into_u8(str);
|
||||
|
||||
@@ -4606,13 +4606,13 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
auto upto_label = [](double z) {
|
||||
char buf[64];
|
||||
::sprintf(buf, "%.2f", z);
|
||||
return _u8L("up to") + " " + std::string(buf) + " " + _u8L("mm");
|
||||
return _u8L("up to") + " " + std::string(buf) + " " + "mm";
|
||||
};
|
||||
|
||||
auto above_label = [](double z) {
|
||||
char buf[64];
|
||||
::sprintf(buf, "%.2f", z);
|
||||
return _u8L("above") + " " + std::string(buf) + " " + _u8L("mm");
|
||||
return _u8L("above") + " " + std::string(buf) + " " + "mm";
|
||||
};
|
||||
|
||||
auto fromto_label = [](double z1, double z2) {
|
||||
@@ -4620,7 +4620,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
::sprintf(buf1, "%.2f", z1);
|
||||
char buf2[64];
|
||||
::sprintf(buf2, "%.2f", z2);
|
||||
return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm");
|
||||
return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + "mm";
|
||||
};
|
||||
|
||||
auto role_time_and_percent = [time_mode](ExtrusionRole role) {
|
||||
|
||||
@@ -4104,6 +4104,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
|
||||
bool any_gizmo_active = m_gizmos.get_current() != nullptr;
|
||||
bool swapMouseButtons = wxGetApp().app_config->get_bool("swap_mouse_buttons");
|
||||
|
||||
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) {
|
||||
m_mouse.drag.move_requires_threshold = false;
|
||||
@@ -4305,7 +4306,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
else if (evt.Dragging() || is_camera_rotate(evt) || is_camera_pan(evt)) {
|
||||
else if (evt.Dragging() || is_camera_rotate(evt, swapMouseButtons) || is_camera_pan(evt, swapMouseButtons)) {
|
||||
m_mouse.dragging = true;
|
||||
|
||||
if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) {
|
||||
@@ -4315,10 +4316,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
}
|
||||
// do not process the dragging if the left mouse was set down in another canvas
|
||||
else if (is_camera_rotate(evt)) {
|
||||
else if (is_camera_rotate(evt, swapMouseButtons)) {
|
||||
// Orca: Sphere rotation for painting view
|
||||
// if dragging over blank area with left button, rotate
|
||||
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
|
||||
// if dragging over blank area with left button or button functions swapped then rotate
|
||||
if ((any_gizmo_active || swapMouseButtons || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
|
||||
Camera& camera = wxGetApp().plater()->get_camera();
|
||||
auto mult_pref = wxGetApp().app_config->get("camera_orbit_mult");
|
||||
const double mult = mult_pref.empty() ? 1.0 : std::stod(mult_pref);
|
||||
@@ -4381,15 +4382,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
camera.auto_type(Camera::EType::Perspective);
|
||||
|
||||
camera.auto_type(Camera::EType::Perspective);
|
||||
m_dirty = true;
|
||||
m_mouse.ignore_right_up = true; // will be reset on button up event even if not right button is pressed
|
||||
}
|
||||
|
||||
m_camera_movement = true;
|
||||
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
|
||||
}
|
||||
else if (is_camera_pan(evt)) {
|
||||
// If dragging over blank area with right button, pan.
|
||||
else if (is_camera_pan(evt, swapMouseButtons)) {
|
||||
// if dragging with right button or if button functions swapped and dragging with left button over blank area then pan
|
||||
if (m_mouse.is_start_position_2D_defined()) {
|
||||
// get point in model space at Z = 0
|
||||
float z = 0.0f;
|
||||
@@ -4407,7 +4410,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
|
||||
camera.set_target(camera.get_target() + orig - cur_pos);
|
||||
m_dirty = true;
|
||||
m_mouse.ignore_right_up = true;
|
||||
m_mouse.ignore_right_up = true; // will be reset on button up event even if not right button is pressed
|
||||
}
|
||||
|
||||
m_camera_movement = true;
|
||||
@@ -4415,10 +4418,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
}
|
||||
else if ((evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) ||
|
||||
(m_camera_movement && !is_camera_rotate(evt) && !is_camera_pan(evt))) {
|
||||
(m_camera_movement && !is_camera_rotate(evt, swapMouseButtons) && !is_camera_pan(evt, swapMouseButtons))) {
|
||||
m_mouse.position = pos.cast<double>();
|
||||
|
||||
if (evt.LeftUp()) {
|
||||
if (swapMouseButtons ? evt.RightUp() : evt.LeftUp()) {
|
||||
m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f;
|
||||
}
|
||||
|
||||
@@ -4603,21 +4606,21 @@ void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
|
||||
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
|
||||
}
|
||||
|
||||
bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt) const
|
||||
bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt, const bool buttonsSwapped) const
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.AltDown() && !evt.ShiftDown();
|
||||
} else {
|
||||
return evt.Dragging() && evt.LeftIsDown();
|
||||
return evt.Dragging() && (buttonsSwapped ? evt.RightIsDown() : evt.LeftIsDown());
|
||||
}
|
||||
}
|
||||
|
||||
bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt) const
|
||||
bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt, const bool buttonsSwapped) const
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.ShiftDown() && !evt.AltDown();
|
||||
} else {
|
||||
return evt.Dragging() && (evt.MiddleIsDown() || evt.RightIsDown());
|
||||
return evt.Dragging() && (evt.MiddleIsDown() || (buttonsSwapped ? evt.LeftIsDown() : evt.RightIsDown()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ class GLCanvas3D
|
||||
};
|
||||
|
||||
static const float THICKNESS_BAR_WIDTH;
|
||||
|
||||
|
||||
// Orca: Shrinkage compensation
|
||||
void set_shrinkage_compensation(const Vec3d &shrinkage_compensation) { m_shrinkage_compensation = shrinkage_compensation; };
|
||||
|
||||
@@ -232,7 +232,7 @@ class GLCanvas3D
|
||||
// Owned by LayersEditing.
|
||||
SlicingParameters* m_slicing_parameters{ nullptr };
|
||||
std::vector<double> m_layer_height_profile;
|
||||
|
||||
|
||||
// Orca: Shrinkage compensation to apply when we need to use object_max_z with Z compensation.
|
||||
Vec3d m_shrinkage_compensation{ Vec3d::Ones() };
|
||||
|
||||
@@ -968,8 +968,8 @@ public:
|
||||
void on_set_focus(wxFocusEvent& evt);
|
||||
void force_set_focus();
|
||||
|
||||
bool is_camera_rotate(const wxMouseEvent& evt) const;
|
||||
bool is_camera_pan(const wxMouseEvent& evt) const;
|
||||
bool is_camera_rotate(const wxMouseEvent& evt, const bool buttonsSwapped) const;
|
||||
bool is_camera_pan(const wxMouseEvent& evt, const bool buttonsSwapped) const;
|
||||
|
||||
Size get_canvas_size() const;
|
||||
Vec2d get_local_mouse_position() const;
|
||||
@@ -1063,7 +1063,7 @@ public:
|
||||
|
||||
bool is_overhang_shown() const { return m_slope.is_GlobalUsed(); }
|
||||
void show_overhang(bool show) { m_slope.globalUse(show); }
|
||||
|
||||
|
||||
bool is_using_slope() const { return m_slope.is_used(); }
|
||||
void use_slope(bool use) { m_slope.use(use); }
|
||||
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
|
||||
|
||||
@@ -104,10 +104,10 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CAT
|
||||
{
|
||||
{ L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1}
|
||||
}},
|
||||
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},
|
||||
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},
|
||||
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},{"top_surface_density", L("Top Surface Density"),1},
|
||||
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},{"bottom_surface_density", L("Bottom Surface Density"),1},
|
||||
{"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lattice_angle_1", "",1},{"lattice_angle_2", "",1},{"infill_overhang_angle", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1},
|
||||
{"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"rotate_solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
|
||||
{"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
|
||||
}},
|
||||
{ L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5},
|
||||
{"enable_overhang_speed", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},
|
||||
@@ -523,7 +523,7 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty
|
||||
|
||||
append_menu_item_add_text(sub_menu, type);
|
||||
append_menu_item_add_svg(sub_menu, type);
|
||||
|
||||
|
||||
return sub_menu;
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ static void append_menu_itemm_add_(const wxString& name, GLGizmosManager::EType
|
||||
} else {
|
||||
svg->create_volume(volume_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (type == ModelVolumeType::MODEL_PART || type == ModelVolumeType::NEGATIVE_VOLUME || type == ModelVolumeType::PARAMETER_MODIFIER ||
|
||||
@@ -1131,7 +1131,7 @@ void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||
|
||||
auto can_edit_text = []() {
|
||||
if (plater() == nullptr)
|
||||
return false;
|
||||
return false;
|
||||
const Selection& selection = plater()->get_selection();
|
||||
if (selection.volumes_count() != 1)
|
||||
return false;
|
||||
@@ -1141,7 +1141,7 @@ void MenuFactory::append_menu_item_edit_text(wxMenu *menu)
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
return volume->is_text();
|
||||
return volume->is_text();
|
||||
};
|
||||
|
||||
if (menu != &m_text_part_menu) {
|
||||
@@ -1168,7 +1168,7 @@ void MenuFactory::append_menu_item_edit_svg(wxMenu *menu)
|
||||
wxString name = _L("Edit SVG");
|
||||
auto can_edit_svg = []() {
|
||||
if (plater() == nullptr)
|
||||
return false;
|
||||
return false;
|
||||
const Selection& selection = plater()->get_selection();
|
||||
if (selection.volumes_count() != 1)
|
||||
return false;
|
||||
@@ -1178,7 +1178,7 @@ void MenuFactory::append_menu_item_edit_svg(wxMenu *menu)
|
||||
const ModelVolume *volume = get_model_volume(*gl_volume, selection.get_model()->objects);
|
||||
if (volume == nullptr)
|
||||
return false;
|
||||
return volume->is_svg();
|
||||
return volume->is_svg();
|
||||
};
|
||||
|
||||
if (menu != &m_svg_part_menu) {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
#include "Widgets/LabeledStaticBox.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "I18N.hpp"
|
||||
@@ -29,7 +31,9 @@ ObjectLayers::ObjectLayers(wxWindow* parent) :
|
||||
|
||||
m_og->activate();
|
||||
m_og->sizer->Clear(true);
|
||||
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, wxOSX ? 0 : 5);
|
||||
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
|
||||
if (auto stb = dynamic_cast<LabeledStaticBox*>(m_og->stb))
|
||||
stb->SetCornerRadius(0);
|
||||
|
||||
m_bmp_delete = ScalableBitmap(parent, "delete");
|
||||
m_bmp_add = ScalableBitmap(parent, "add");
|
||||
@@ -74,7 +78,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||
};
|
||||
|
||||
// Add text
|
||||
auto head_text = new wxStaticText(m_parent, wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
auto head_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
head_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
head_text->SetFont(wxGetApp().normal_font());
|
||||
m_grid_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -105,7 +109,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||
|
||||
m_grid_sizer->Add(editor, 1, wxEXPAND);
|
||||
|
||||
auto middle_text = new wxStaticText(m_parent, wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
auto middle_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
middle_text->SetFont(wxGetApp().normal_font());
|
||||
m_grid_sizer->Add(middle_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -135,12 +139,12 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||
m_grid_sizer->Add(editor, 1, wxEXPAND);
|
||||
|
||||
auto sizer2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto unit_text = new wxStaticText(m_parent, wxID_ANY, _L("mm"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
auto unit_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, "mm", wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
unit_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
unit_text->SetFont(wxGetApp().normal_font());
|
||||
sizer2->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
m_grid_sizer->Add(sizer2);
|
||||
m_grid_sizer->Add(sizer2, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// BBS
|
||||
// Add control for the "Layer height"
|
||||
@@ -156,7 +160,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||
//auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
//sizer->Add(editor);
|
||||
|
||||
//auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm"));
|
||||
//auto temp = new wxStaticText(m_parent, wxID_ANY, "mm");
|
||||
//temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
//temp->SetFont(wxGetApp().normal_font());
|
||||
//sizer->Add(temp, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit());
|
||||
@@ -170,12 +174,12 @@ void ObjectLayers::create_layers_list()
|
||||
{
|
||||
for (const auto &layer : m_object->layer_config_ranges) {
|
||||
const t_layer_height_range& range = layer.first;
|
||||
auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
|
||||
auto del_btn = new PlusMinusButton(m_og->ctrl_parent(), m_bmp_delete, range);
|
||||
del_btn->DisableFocusFromKeyboard();
|
||||
del_btn->SetBackgroundColour(m_parent->GetBackgroundColour());
|
||||
del_btn->SetToolTip(_L("Remove height range"));
|
||||
|
||||
auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range);
|
||||
auto add_btn = new PlusMinusButton(m_og->ctrl_parent(), m_bmp_add, range);
|
||||
add_btn->DisableFocusFromKeyboard();
|
||||
add_btn->SetBackgroundColour(m_parent->GetBackgroundColour());
|
||||
wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range);
|
||||
@@ -183,8 +187,10 @@ void ObjectLayers::create_layers_list()
|
||||
add_btn->Enable(tooltip.IsEmpty());
|
||||
|
||||
auto sizer = create_layer(range, del_btn, add_btn);
|
||||
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
|
||||
sizer->Add(add_btn);
|
||||
auto b_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
b_sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
|
||||
b_sizer->Add(add_btn);
|
||||
sizer->Add(b_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, m_parent->FromDIP(1)); // aligns +/- buttons vertically since we got 1px gap on bottom of icons
|
||||
|
||||
del_btn->Bind(wxEVT_BUTTON, [del_btn](wxEvent &) {
|
||||
wxGetApp().obj_list()->del_layer_range(del_btn->range);
|
||||
@@ -218,6 +224,8 @@ void ObjectLayers::update_layers_list()
|
||||
// only call sizer->Clear(true) via CallAfter, otherwise crash happens in Linux when press enter in Height Range
|
||||
// because an element cannot be destroyed while there are pending events for this element.(https://github.com/wxWidgets/Phoenix/issues/1854)
|
||||
wxGetApp().CallAfter([this, type, objects_ctrl, range]() {
|
||||
m_og->ctrl_parent()->Freeze();
|
||||
|
||||
// Delete all controls from options group
|
||||
m_grid_sizer->Clear(true);
|
||||
|
||||
@@ -228,8 +236,10 @@ void ObjectLayers::update_layers_list()
|
||||
else
|
||||
create_layer(range, nullptr, nullptr);
|
||||
|
||||
m_og->ctrl_parent()->Thaw();
|
||||
|
||||
m_parent->Layout();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectLayers::update_scene_from_editor_selection() const
|
||||
@@ -340,7 +350,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
|
||||
m_valid_value(value),
|
||||
m_type(type),
|
||||
m_set_focus_data(set_focus_data_fn),
|
||||
wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
|
||||
wxTextCtrl(parent->m_og->ctrl_parent(), wxID_ANY, value, wxDefaultPosition,
|
||||
wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
|
||||
#ifdef _WIN32
|
||||
| wxBORDER_SIMPLE
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "SingleChoiceDialog.hpp"
|
||||
#include "StepMeshDialog.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <wx/progdlg.h>
|
||||
@@ -2031,15 +2032,49 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo
|
||||
dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())),
|
||||
_L("Loading file") + ": " + from_path(boost::filesystem::path(input_file).filename()));
|
||||
dlg.Fit();
|
||||
|
||||
|
||||
bool is_user_cancel = false;
|
||||
Model model;
|
||||
try {
|
||||
model = Model::read_from_file(input_file, nullptr, nullptr, LoadStrategy::LoadModel);
|
||||
if (boost::iends_with(input_file, ".stp") ||
|
||||
boost::iends_with(input_file, ".step")) {
|
||||
double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion"));
|
||||
if (linear <= 0) linear = 0.003;
|
||||
double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion"));
|
||||
if (angle <= 0) angle = 0.5;
|
||||
bool split_compound = wxGetApp().app_config->get_bool("is_split_compound");
|
||||
model = Model::read_from_step(
|
||||
input_file, LoadStrategy::LoadModel, nullptr, nullptr,
|
||||
[this, &is_user_cancel, &linear, &angle, &split_compound](Slic3r::Step& file, double& linear_value,
|
||||
double& angle_value, bool& is_split) -> int {
|
||||
if (wxGetApp().app_config->get_bool("enable_step_mesh_setting")) {
|
||||
StepMeshDialog mesh_dlg(nullptr, file, linear, angle);
|
||||
if (mesh_dlg.ShowModal() == wxID_OK) {
|
||||
linear_value = mesh_dlg.get_linear_defletion();
|
||||
angle_value = mesh_dlg.get_angle_defletion();
|
||||
is_split = mesh_dlg.get_split_compound_value();
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
linear_value = linear;
|
||||
angle_value = angle;
|
||||
is_split = split_compound;
|
||||
return 1;
|
||||
}
|
||||
is_user_cancel = true;
|
||||
return -1;
|
||||
},
|
||||
linear, angle, split_compound);
|
||||
} else {
|
||||
model = Model::read_from_file(input_file, nullptr, nullptr, LoadStrategy::LoadModel);
|
||||
}
|
||||
}
|
||||
catch (std::exception&) {
|
||||
// auto msg = _L("Error!") + " " + input_file + " : " + e.what() + ".";
|
||||
auto msg = _L("Error!") + " " + _L("Failed to get the model data in the current file.");
|
||||
show_error(parent, msg);
|
||||
if (!is_user_cancel) {
|
||||
// auto msg = _L("Error!") + " " + input_file + " : " + e.what() + ".";
|
||||
auto msg = _L("Error!") + " " + _L("Failed to get the model data in the current file.");
|
||||
show_error(parent, msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2537,19 +2537,19 @@ void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay
|
||||
{
|
||||
sort_notifications();
|
||||
|
||||
float bottom_up_last_y = bottom_margin * m_scale;
|
||||
float bottom_up_last_y = bottom_margin; // ORCA dont scale margins
|
||||
|
||||
int i = 0;
|
||||
for (const auto& notification : m_pop_notifications) {
|
||||
if (notification->get_data().level == NotificationLevel::ErrorNotificationLevel || notification->get_data().level == NotificationLevel::SeriousWarningNotificationLevel) {
|
||||
notification->bbl_render_block_notification(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale);
|
||||
notification->bbl_render_block_notification(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin); // ORCA dont scale margins
|
||||
if (notification->get_state() != PopNotification::EState::Finished)
|
||||
bottom_up_last_y = notification->get_top() + GAP_WIDTH;
|
||||
}
|
||||
else {
|
||||
if (notification->get_state() != PopNotification::EState::Hidden && notification->get_state() != PopNotification::EState::Finished) {
|
||||
i++;
|
||||
notification->render(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale);
|
||||
notification->render(canvas, bottom_up_last_y, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin); // ORCA dont scale margins
|
||||
if (notification->get_state() != PopNotification::EState::Finished)
|
||||
bottom_up_last_y = notification->get_top() + GAP_WIDTH;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
|
||||
parent->GetNthChild(i)->SetIdx(i + 1);
|
||||
}
|
||||
const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str();
|
||||
m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")";
|
||||
m_name = _(L("Range")) + label_range + "(" + _("mm") + ")";
|
||||
m_bmp = create_scaled_bitmap(LayerIcon);
|
||||
|
||||
set_icons();
|
||||
|
||||
@@ -9617,7 +9617,7 @@ void Plater::_calib_pa_pattern(const Calib_Params& params)
|
||||
|
||||
speeds.assign({speed});
|
||||
const auto msg{_L("INFO:") + "\n" +
|
||||
_L("No speeds provided for calibration. Use default optimal speed ") + std::to_string(long(speed)) + _L("mm/s")};
|
||||
_L("No speeds provided for calibration. Use default optimal speed ") + std::to_string(long(speed)) + "mm/s"};
|
||||
get_notification_manager()->push_notification(msg.ToStdString());
|
||||
} else if (speeds.size() == 1) {
|
||||
// If we have single value provided, set speed using global configuration.
|
||||
@@ -9919,7 +9919,6 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i
|
||||
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
|
||||
_obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45));
|
||||
_obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135));
|
||||
_obj->config.set_key_value("rotate_solid_infill_direction", new ConfigOptionBool(true));
|
||||
_obj->config.set_key_value("ironing_type", new ConfigOptionEnum<IroningType>(IroningType::NoIroning));
|
||||
_obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloat(internal_solid_speed));
|
||||
_obj->config.set_key_value("top_surface_speed", new ConfigOptionFloat(top_surface_speed));
|
||||
|
||||
@@ -123,7 +123,7 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxWindow *pa
|
||||
auto current_setting = app_config->get(param);
|
||||
if (!current_setting.empty()) {
|
||||
auto compare = [current_setting](string possible_setting) { return current_setting == possible_setting; };
|
||||
auto iterator = find_if(config_name_index.begin(), config_name_index.end(), compare);
|
||||
auto iterator = find_if(config_name_index.begin(), config_name_index.end(), compare);
|
||||
current_index = iterator - config_name_index.begin();
|
||||
}
|
||||
|
||||
@@ -536,7 +536,7 @@ wxBoxSizer *PreferencesDialog::create_camera_orbit_mult_input(wxString title, wx
|
||||
sizer_input->Add(input_title, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3);
|
||||
sizer_input->Add(input, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
sizer_input->Add(0, 0, 0, wxEXPAND | wxLEFT, 3);
|
||||
|
||||
|
||||
const double min = 0.05;
|
||||
const double max = 2.0;
|
||||
|
||||
@@ -1207,12 +1207,12 @@ wxWindow* PreferencesDialog::create_general_page()
|
||||
|
||||
std::vector<wxString> Units = {_L("Metric") + " (mm, g)", _L("Imperial") + " (in, oz)"};
|
||||
auto item_currency = create_item_combobox(_L("Units"), page, _L("Units"), "use_inches", Units);
|
||||
auto item_single_instance = create_item_checkbox(_L("Allow only one OrcaSlicer instance"), page,
|
||||
auto item_single_instance = create_item_checkbox(_L("Allow only one OrcaSlicer instance"), page,
|
||||
#if __APPLE__
|
||||
_L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances "
|
||||
"of same app from the command line. In such case this settings will allow only one instance."),
|
||||
"of same app from the command line. In such case this settings will allow only one instance."),
|
||||
#else
|
||||
_L("If this is enabled, when starting OrcaSlicer and another instance of the same OrcaSlicer is already running, that instance will be reactivated instead."),
|
||||
_L("If this is enabled, when starting OrcaSlicer and another instance of the same OrcaSlicer is already running, that instance will be reactivated instead."),
|
||||
#endif
|
||||
50, "single_instance");
|
||||
|
||||
@@ -1224,6 +1224,7 @@ wxWindow* PreferencesDialog::create_general_page()
|
||||
|
||||
auto item_mouse_zoom_settings = create_item_checkbox(_L("Zoom to mouse position"), page, _L("Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center."), 50, "zoom_to_mouse");
|
||||
auto item_use_free_camera_settings = create_item_checkbox(_L("Use free camera"), page, _L("If enabled, use free camera. If not enabled, use constrained camera."), 50, "use_free_camera");
|
||||
auto swap_pan_rotate = create_item_checkbox(_L("Swap pan and rotate mouse buttons"), page, _L("If enabled, swaps the left and right mouse buttons pan and rotate functions."), 50, "swap_mouse_buttons");
|
||||
auto reverse_mouse_zoom = create_item_checkbox(_L("Reverse mouse zoom"), page, _L("If enabled, reverses the direction of zoom with mouse wheel."), 50, "reverse_mouse_wheel_zoom");
|
||||
auto camera_orbit_mult = create_camera_orbit_mult_input(_L("Orbit speed multiplier"), page, _L("Multiplies the orbit speed for finer or coarser camera movement."));
|
||||
|
||||
@@ -1308,6 +1309,7 @@ wxWindow* PreferencesDialog::create_general_page()
|
||||
sizer_page->Add(item_single_instance, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_use_free_camera_settings, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(swap_pan_rotate, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(reverse_mouse_zoom, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(camera_orbit_mult, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_show_splash_screen, 0, wxTOP, FromDIP(3));
|
||||
|
||||
@@ -89,10 +89,10 @@ void Chart::draw() {
|
||||
}
|
||||
|
||||
// axis labels:
|
||||
wxString label = _(L("Time")) + " ("+_(L("s"))+")";
|
||||
wxString label = _(L("Time")) + " ("+_("s")+")";
|
||||
dc.GetTextExtent(label,&text_width,&text_height);
|
||||
dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+0.6*legend_side));
|
||||
label = _(L("Volumetric speed")) + " (" + _(L("mm³/s")) + ")";
|
||||
label = _(L("Volumetric speed")) + " (" + _("mm³/s") + ")";
|
||||
dc.GetTextExtent(label,&text_width,&text_height);
|
||||
dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90);
|
||||
}
|
||||
|
||||
+90
-37
@@ -811,18 +811,18 @@ void Tab::decorate()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sys_page) {
|
||||
if (!sys_page) {
|
||||
is_nonsys_value = true;
|
||||
sys_icon = m_bmp_non_system;
|
||||
sys_tt = m_tt_non_system;
|
||||
|
||||
if (!modified_page)
|
||||
if (!modified_page)
|
||||
color = &m_default_text_clr;
|
||||
else
|
||||
else
|
||||
color = &m_modified_label_clr;
|
||||
}
|
||||
|
||||
if (!modified_page) {
|
||||
if (!modified_page) {
|
||||
is_modified_value = false;
|
||||
icon = &m_bmp_white_bullet;
|
||||
tt = &m_tt_white_bullet;
|
||||
@@ -1470,8 +1470,11 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
|
||||
update_wiping_button_visibility();
|
||||
|
||||
if (opt_key == "pellet_flow_coefficient")
|
||||
|
||||
if (opt_key == "pellet_flow_coefficient")
|
||||
{
|
||||
double double_value = Preset::convert_pellet_flow_to_filament_diameter(boost::any_cast<double>(value));
|
||||
m_config->set_key_value("filament_diameter", new ConfigOptionFloats{double_value});
|
||||
@@ -1481,6 +1484,44 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
double double_value = Preset::convert_filament_diameter_to_pellet_flow(boost::any_cast<double>(value));
|
||||
m_config->set_key_value("pellet_flow_coefficient", new ConfigOptionFloats{double_value});
|
||||
}
|
||||
|
||||
|
||||
if (opt_key == "single_extruder_multi_material" ){
|
||||
const auto bSEMM = m_config->opt_bool("single_extruder_multi_material");
|
||||
wxGetApp().sidebar().show_SEMM_buttons(bSEMM);
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
|
||||
}
|
||||
|
||||
if(opt_key == "purge_in_prime_tower")
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
|
||||
|
||||
|
||||
if (opt_key == "enable_prime_tower") {
|
||||
auto timelapse_type = m_config->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
|
||||
bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth;
|
||||
if (!boost::any_cast<bool>(value) && timelapse_enabled) {
|
||||
MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
|
||||
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_NO) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
}
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
bool is_precise_z_height = m_config->option<ConfigOptionBool>("precise_z_height")->value;
|
||||
if (boost::any_cast<bool>(value) && is_precise_z_height) {
|
||||
MessageDialog dlg(wxGetApp().plater(), _L("Enabling both precise Z height and the prime tower may cause the size of prime tower to increase. Do you still want to enable?"),
|
||||
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_NO) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(false));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
}
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
update_wiping_button_visibility();
|
||||
}
|
||||
|
||||
|
||||
if (opt_key == "single_extruder_multi_material" ){
|
||||
@@ -1591,7 +1632,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(opt_key=="layer_height"){
|
||||
auto min_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("min_layer_height")->values;
|
||||
auto max_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("max_layer_height")->values;
|
||||
@@ -1659,7 +1700,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
if (opt_key == "filament_long_retractions_when_cut"){
|
||||
unsigned char activate = boost::any_cast<unsigned char>(value);
|
||||
if (activate == 1) {
|
||||
MessageDialog dialog(wxGetApp().plater(),
|
||||
MessageDialog dialog(wxGetApp().plater(),
|
||||
_L("Experimental feature: Retracting and cutting off the filament at a greater distance during filament changes to minimize flush. "
|
||||
"Although it can notably reduce flush, it may also elevate the risk of nozzle clogs or other printing complications. "
|
||||
"Please use with the latest printer firmware."), "", wxICON_WARNING | wxOK);
|
||||
@@ -2156,29 +2197,41 @@ void TabPrint::build()
|
||||
optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell");
|
||||
optgroup->append_single_option_line("top_shell_layers");
|
||||
optgroup->append_single_option_line("top_shell_thickness");
|
||||
optgroup->append_single_option_line("top_surface_density");
|
||||
optgroup->append_single_option_line("top_surface_pattern");
|
||||
optgroup->append_single_option_line("bottom_shell_layers");
|
||||
optgroup->append_single_option_line("bottom_shell_thickness");
|
||||
optgroup->append_single_option_line("bottom_surface_density");
|
||||
optgroup->append_single_option_line("bottom_surface_pattern");
|
||||
optgroup->append_single_option_line("top_bottom_infill_wall_overlap");
|
||||
|
||||
optgroup = page->new_optgroup(L("Infill"), L"param_infill");
|
||||
optgroup->append_single_option_line("sparse_infill_density", "strength_settings_infill#sparse-infill-density");
|
||||
optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern");
|
||||
optgroup->append_single_option_line("infill_direction");
|
||||
optgroup->append_single_option_line("sparse_infill_rotate_template");
|
||||
optgroup->append_single_option_line("skin_infill_density");
|
||||
optgroup->append_single_option_line("skeleton_infill_density");
|
||||
optgroup->append_single_option_line("infill_lock_depth");
|
||||
optgroup->append_single_option_line("skin_infill_depth");
|
||||
optgroup->append_single_option_line("skin_infill_line_width", "parameter/line-width");
|
||||
optgroup->append_single_option_line("skeleton_infill_line_width", "parameter/line-width");
|
||||
optgroup->append_single_option_line("symmetric_infill_y_axis");
|
||||
optgroup->append_single_option_line("infill_shift_step");
|
||||
|
||||
optgroup->append_single_option_line("lattice_angle_1");
|
||||
optgroup->append_single_option_line("lattice_angle_2");
|
||||
optgroup->append_single_option_line("infill_overhang_angle");
|
||||
optgroup->append_single_option_line("infill_anchor_max");
|
||||
optgroup->append_single_option_line("infill_anchor");
|
||||
optgroup->append_single_option_line("internal_solid_infill_pattern");
|
||||
optgroup->append_single_option_line("solid_infill_direction");
|
||||
optgroup->append_single_option_line("solid_infill_rotate_template");
|
||||
optgroup->append_single_option_line("gap_fill_target");
|
||||
optgroup->append_single_option_line("filter_out_gap_fill");
|
||||
optgroup->append_single_option_line("infill_wall_overlap");
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
||||
optgroup->append_single_option_line("infill_direction");
|
||||
optgroup->append_single_option_line("solid_infill_direction");
|
||||
optgroup->append_single_option_line("rotate_solid_infill_direction");
|
||||
optgroup->append_single_option_line("bridge_angle");
|
||||
optgroup->append_single_option_line("internal_bridge_angle"); // ORCA: Internal bridge angle override
|
||||
optgroup->append_single_option_line("minimum_sparse_infill_area");
|
||||
@@ -2284,7 +2337,7 @@ void TabPrint::build()
|
||||
|
||||
//optgroup = page->new_optgroup(L("Options for support material and raft"));
|
||||
|
||||
// Support
|
||||
// Support
|
||||
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
||||
optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance");
|
||||
optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance");
|
||||
@@ -2320,7 +2373,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("tree_support_adaptive_layer_height");
|
||||
optgroup->append_single_option_line("tree_support_auto_brim");
|
||||
optgroup->append_single_option_line("tree_support_brim_width");
|
||||
|
||||
|
||||
page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders
|
||||
optgroup = page->new_optgroup(L("Prime tower"), L"param_tower");
|
||||
optgroup->append_single_option_line("enable_prime_tower");
|
||||
@@ -2419,7 +2472,7 @@ page = add_options_page(L("Others"), "custom-gcode_other"); // ORCA: icon only v
|
||||
option.opt.multiline = true;
|
||||
// option.opt.height = 5;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
|
||||
optgroup = page->new_optgroup(L("Post-processing Scripts"), L"param_gcode", 0);
|
||||
option = optgroup->get_option("post_process");
|
||||
option.opt.full_width = true;
|
||||
@@ -2440,7 +2493,7 @@ page = add_options_page(L("Others"), "custom-gcode_other"); // ORCA: icon only v
|
||||
// create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) {
|
||||
// return compatible_widget_create(parent, m_compatible_printers);
|
||||
// });
|
||||
|
||||
|
||||
// option = optgroup->get_option("compatible_printers_condition");
|
||||
// option.opt.full_width = true;
|
||||
// optgroup->append_single_option_line(option);
|
||||
@@ -2849,7 +2902,7 @@ void TabPrintPlate::build()
|
||||
auto page = add_options_page(L("Plate Settings"), "empty");
|
||||
auto optgroup = page->new_optgroup("");
|
||||
optgroup->append_single_option_line("curr_bed_type");
|
||||
optgroup->append_single_option_line("skirt_start_angle");
|
||||
optgroup->append_single_option_line("skirt_start_angle");
|
||||
optgroup->append_single_option_line("print_sequence");
|
||||
optgroup->append_single_option_line("spiral_mode");
|
||||
optgroup->append_single_option_line("first_layer_sequence_choice");
|
||||
@@ -3000,7 +3053,7 @@ void TabPrintPlate::notify_changed(ObjectBase* object)
|
||||
for (auto item : items) {
|
||||
if (objects_list->GetModel()->GetItemType(item) == itPlate) {
|
||||
ObjectDataViewModelNode* node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
||||
if (node)
|
||||
if (node)
|
||||
node->set_action_icon(!m_all_keys.empty());
|
||||
}
|
||||
}
|
||||
@@ -3008,7 +3061,7 @@ void TabPrintPlate::notify_changed(ObjectBase* object)
|
||||
|
||||
void TabPrintPlate::update_custom_dirty()
|
||||
{
|
||||
for (auto k : m_null_keys)
|
||||
for (auto k : m_null_keys)
|
||||
m_options_list[k] = 0;
|
||||
for (auto k : m_all_keys) {
|
||||
if (k == "first_layer_sequence_choice" || k == "other_layers_sequence_choice") {
|
||||
@@ -3250,9 +3303,9 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print
|
||||
|
||||
std::vector<std::string> opt_keys = { "filament_retraction_length",
|
||||
"filament_z_hop",
|
||||
"filament_z_hop_types",
|
||||
"filament_z_hop_types",
|
||||
"filament_retract_lift_above",
|
||||
"filament_retract_lift_below",
|
||||
"filament_retract_lift_below",
|
||||
"filament_retract_lift_enforce",
|
||||
"filament_retraction_speed",
|
||||
"filament_deretraction_speed",
|
||||
@@ -3363,7 +3416,7 @@ void TabFilament::build()
|
||||
optgroup->append_single_option_line("adaptive_pressure_advance");
|
||||
optgroup->append_single_option_line("adaptive_pressure_advance_overhangs");
|
||||
optgroup->append_single_option_line("adaptive_pressure_advance_bridges");
|
||||
|
||||
|
||||
Option option = optgroup->get_option("adaptive_pressure_advance_model");
|
||||
option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
@@ -3675,7 +3728,7 @@ void TabFilament::toggle_options()
|
||||
toggle_option(el, has_enable_overhang_bridge_fan);
|
||||
|
||||
toggle_option("additional_cooling_fan_speed", cfg.opt_bool("auxiliary_fan"));
|
||||
|
||||
|
||||
// Orca: toggle dont slow down for external perimeters if
|
||||
bool has_slow_down_for_layer_cooling = m_config->opt_bool("slow_down_for_layer_cooling", 0);
|
||||
toggle_option("dont_slow_down_outer_wall", has_slow_down_for_layer_cooling);
|
||||
@@ -3688,7 +3741,7 @@ void TabFilament::toggle_options()
|
||||
//Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type.
|
||||
DynamicConfig& proj_cfg = m_preset_bundle->project_config;
|
||||
std::string bed_temp_1st_layer_key = "";
|
||||
if (proj_cfg.has("curr_bed_type"))
|
||||
if (proj_cfg.has("curr_bed_type"))
|
||||
{
|
||||
bed_temp_1st_layer_key = get_bed_temp_1st_layer_key(proj_cfg.opt_enum<BedType>("curr_bed_type"));
|
||||
}
|
||||
@@ -3701,13 +3754,13 @@ void TabFilament::toggle_options()
|
||||
bed_temp_keys.end() ||
|
||||
is_BBL_printer || cfg.opt_bool("support_multi_bed_types");
|
||||
|
||||
for (const auto& key : bed_temp_keys)
|
||||
for (const auto& key : bed_temp_keys)
|
||||
{
|
||||
toggle_line(key, support_multi_bed_types || bed_temp_1st_layer_key == key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Orca: adaptive pressure advance and calibration model
|
||||
// If PA is not enabled, disable adaptive pressure advance and hide the model section
|
||||
// If adaptive PA is not enabled, hide the adaptive PA model section
|
||||
@@ -3891,7 +3944,7 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("use_firmware_retraction");
|
||||
// optgroup->append_single_option_line("spaghetti_detector");
|
||||
optgroup->append_single_option_line("time_cost");
|
||||
|
||||
|
||||
optgroup = page->new_optgroup(L("Cooling Fan"), "param_cooling_fan");
|
||||
Line line = Line{ L("Fan speed-up time"), optgroup->get_option("fan_speedup_time").opt.tooltip };
|
||||
line.append_option(optgroup->get_option("fan_speedup_time"));
|
||||
@@ -3954,8 +4007,8 @@ void TabPrinter::build_fff()
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = gcode_field_height; // 150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
|
||||
|
||||
|
||||
optgroup = page->new_optgroup(L("Before layer change G-code"),"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
|
||||
@@ -3977,7 +4030,7 @@ void TabPrinter::build_fff()
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = gcode_field_height;//150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
|
||||
optgroup = page->new_optgroup(L("Timelapse G-code"), L"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
|
||||
@@ -4172,7 +4225,7 @@ PageShp TabPrinter::build_kinematics_page()
|
||||
}
|
||||
auto optgroup = page->new_optgroup(L("Advanced"), "param_advanced");
|
||||
optgroup->append_single_option_line("emit_machine_limits_to_gcode");
|
||||
|
||||
|
||||
// resonance avoidance ported over from qidi slicer
|
||||
optgroup = page->new_optgroup(L("Resonance Avoidance"));
|
||||
optgroup->append_single_option_line("resonance_avoidance");
|
||||
@@ -4711,7 +4764,7 @@ void TabPrinter::toggle_options()
|
||||
toggle_option("long_retractions_when_cut", !use_firmware_retraction && m_config->opt_int("enable_long_retraction_when_cut"),i);
|
||||
toggle_line("retraction_distances_when_cut#0", m_config->opt_bool("long_retractions_when_cut", i));
|
||||
//toggle_option("retraction_distances_when_cut", m_config->opt_bool("long_retractions_when_cut",i),i);
|
||||
|
||||
|
||||
toggle_option("travel_slope", m_config->opt_enum("z_hop_types", i) != ZHopType::zhtNormal, i);
|
||||
}
|
||||
|
||||
@@ -5123,13 +5176,13 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
||||
try {
|
||||
//BBS delete preset
|
||||
Preset ¤t_preset = m_presets->get_selected_preset();
|
||||
|
||||
|
||||
// Obtain compatible filament and process presets for printers
|
||||
if (m_preset_bundle && m_presets->get_preset_base(current_preset) == ¤t_preset && printer_tab && !current_preset.is_system) {
|
||||
delete_third_printer = true;
|
||||
for (const Preset &preset : m_preset_bundle->filaments.get_presets()) {
|
||||
if (preset.is_compatible && !preset.is_default) {
|
||||
if (preset.inherits() != "")
|
||||
if (preset.inherits() != "")
|
||||
filament_presets.push_front(preset);
|
||||
else
|
||||
filament_presets.push_back(preset);
|
||||
@@ -5260,7 +5313,7 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (technology_changed)
|
||||
@@ -5862,7 +5915,7 @@ void Tab::delete_preset()
|
||||
//wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal())
|
||||
wxID_YES == MessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()))
|
||||
return;
|
||||
|
||||
|
||||
// if we just delete preset from the physical printer
|
||||
if (m_presets_choice->is_selected_physical_printer()) {
|
||||
PhysicalPrinter& printer = physical_printers.get_selected_printer();
|
||||
@@ -5986,7 +6039,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
|
||||
deps.checkbox->SetValue(state);
|
||||
deps.btn->Enable(!state);
|
||||
// All printers have been made compatible with this preset.
|
||||
if (state)
|
||||
if (state)
|
||||
this->load_key_value(deps.key_list, std::vector<std::string> {});
|
||||
this->get_field(deps.key_condition)->toggle(state);
|
||||
this->update_changed_ui();
|
||||
@@ -6145,7 +6198,7 @@ void TabPrinter::cache_extruder_cnt(const DynamicPrintConfig* config/* = nullptr
|
||||
if (Preset::printer_technology(cached_config) == ptSLA)
|
||||
return;
|
||||
|
||||
// get extruders count
|
||||
// get extruders count
|
||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(cached_config.option("nozzle_diameter"));
|
||||
m_cache_extruder_count = nozzle_diameter->values.size(); //m_extruders_count;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
#include "../GUI_Utils.hpp"
|
||||
#include "Label.hpp"
|
||||
|
||||
/*
|
||||
Fix label overflowing to inner frame
|
||||
Fix use elypsis if text too long
|
||||
setmin size
|
||||
*/
|
||||
|
||||
LabeledStaticBox::LabeledStaticBox()
|
||||
: state_handler(this)
|
||||
{
|
||||
@@ -28,7 +22,6 @@ LabeledStaticBox::LabeledStaticBox()
|
||||
std::make_pair(0xDBDBDB, (int) StateColor::Normal),
|
||||
std::make_pair(0xDBDBDB, (int) StateColor::Disabled)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
LabeledStaticBox::LabeledStaticBox(
|
||||
@@ -63,11 +56,10 @@ bool LabeledStaticBox::Create(
|
||||
m_pos = this->GetPosition();
|
||||
|
||||
int tW,tH,descent,externalLeading;
|
||||
GetTextExtent("Yy", &tW, &tH, &descent, &externalLeading, &m_font);
|
||||
// empty label sets m_label_height as 0 that causes extra spacing at top
|
||||
GetTextExtent(m_label.IsEmpty() ? "Orca" : m_label, &tW, &tH, &descent, &externalLeading, &m_font);
|
||||
m_label_height = tH - externalLeading;
|
||||
|
||||
GetTextExtent(m_label, &tW, &tH, &descent, &externalLeading, &m_font);
|
||||
m_label_width = tW;
|
||||
m_label_width = tW;
|
||||
|
||||
Bind(wxEVT_PAINT,([this](wxPaintEvent e) {
|
||||
wxPaintDC dc(this);
|
||||
@@ -178,4 +170,11 @@ void LabeledStaticBox::DrawBorderAndLabel(wxDC& dc)
|
||||
dc.SetTextForeground(text_color.colorForStates(state_handler.states()));
|
||||
dc.DrawText(m_label, wxPoint(10 * m_scale, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void LabeledStaticBox::GetBordersForSizer(int* borderTop, int* borderOther) const {
|
||||
wxStaticBox::GetBordersForSizer(borderTop, borderOther);
|
||||
#ifdef __WXOSX__
|
||||
*borderOther = 5; // Make sure macOS uses the same border padding as other platforms
|
||||
#endif
|
||||
}
|
||||
@@ -63,6 +63,9 @@ protected:
|
||||
int m_label_width;
|
||||
float m_scale;
|
||||
wxPoint m_pos;
|
||||
|
||||
void GetBordersForSizer(int *borderTop, int *borderOther) const override;
|
||||
|
||||
};
|
||||
|
||||
#endif // !slic3r_GUI_LabeledStaticBox_hpp_
|
||||
@@ -115,10 +115,10 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
|
||||
update_ui(m_chart);
|
||||
sizer_chart->Add(m_chart, 0, wxALL, 5);
|
||||
|
||||
m_widget_time = new SpinInput(this, wxEmptyString, _L("ms") , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 0 , 5000 , 3000, 500);
|
||||
m_widget_volume = new SpinInput(this, wxEmptyString, _L("mm³"), wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 0 , 10000, 0 );
|
||||
m_widget_ramming_line_width_multiplicator = new SpinInput(this, wxEmptyString, _L("%") , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 10, 200 , 100 );
|
||||
m_widget_ramming_step_multiplicator = new SpinInput(this, wxEmptyString, _L("%") , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 10, 200 , 100 );
|
||||
m_widget_time = new SpinInput(this, wxEmptyString, "ms" , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 0 , 5000 , 3000, 500);
|
||||
m_widget_volume = new SpinInput(this, wxEmptyString, "mm³", wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 0 , 10000, 0 );
|
||||
m_widget_ramming_line_width_multiplicator = new SpinInput(this, wxEmptyString, "%" , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 10, 200 , 100 );
|
||||
m_widget_ramming_step_multiplicator = new SpinInput(this, wxEmptyString, "%" , wxDefaultPosition, wxSize(scale(120), -1), wxSP_ARROW_KEYS, 10, 200 , 100 );
|
||||
|
||||
auto add_title = [this, sizer_param](wxString label){
|
||||
auto title = new StaticLine(this, 0, label);
|
||||
|
||||
@@ -306,7 +306,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
// start temp
|
||||
auto start_temp_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto start_temp_text = new wxStaticText(this, wxID_ANY, start_temp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStart = new TextInput(this, std::to_string(230), _L("\u2103"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStart = new TextInput(this, std::to_string(230), "\u2103" /* °C */, "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
start_temp_sizer->Add(start_temp_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
start_temp_sizer->Add(m_tiStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -315,7 +315,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
// end temp
|
||||
auto end_temp_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_temp_text = new wxStaticText(this, wxID_ANY, end_temp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiEnd = new TextInput(this, std::to_string(190), _L("\u2103"), "", wxDefaultPosition, ti_size);
|
||||
m_tiEnd = new TextInput(this, std::to_string(190), "\u2103" /* °C */, "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_temp_sizer->Add(end_temp_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
end_temp_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -324,7 +324,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
// temp step
|
||||
auto temp_step_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto temp_step_text = new wxStaticText(this, wxID_ANY, temp_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(5),_L("\u2103"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(5),"\u2103" /* °C */, "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
m_tiStep->Enable(false);
|
||||
temp_step_sizer->Add(temp_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -353,7 +353,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
if(!ti->GetTextCtrl()->GetValue().ToULong(&t))
|
||||
return;
|
||||
if(t> 350 || t < 170){
|
||||
MessageDialog msg_dlg(nullptr, wxString::Format(L"Supported range: 170%s - 350%s",_L("\u2103"),_L("\u2103")), wxEmptyString, wxICON_WARNING | wxOK);
|
||||
MessageDialog msg_dlg(nullptr, wxString::Format(L"Supported range: 170%s - 350%s","\u2103" /* °C */,"\u2103" /* °C */), wxEmptyString, wxICON_WARNING | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
if(t > 350)
|
||||
t = 350;
|
||||
@@ -479,7 +479,7 @@ MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWin
|
||||
// start vol
|
||||
auto start_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto start_vol_text = new wxStaticText(this, wxID_ANY, start_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStart = new TextInput(this, std::to_string(5), _L("mm³/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStart = new TextInput(this, std::to_string(5), "mm³/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
|
||||
start_vol_sizer->Add(start_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -489,7 +489,7 @@ MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWin
|
||||
// end vol
|
||||
auto end_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_vol_text = new wxStaticText(this, wxID_ANY, end_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiEnd = new TextInput(this, std::to_string(20), _L("mm³/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiEnd = new TextInput(this, std::to_string(20), "mm³/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_vol_sizer->Add(end_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
end_vol_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -498,7 +498,7 @@ MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWin
|
||||
// vol step
|
||||
auto vol_step_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto vol_step_text = new wxStaticText(this, wxID_ANY, vol_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(0.5), _L("mm³/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(0.5), "mm³/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
vol_step_sizer->Add(vol_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
vol_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -578,7 +578,7 @@ VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
// start vol
|
||||
auto start_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto start_vol_text = new wxStaticText(this, wxID_ANY, start_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStart = new TextInput(this, std::to_string(40), _L("mm/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStart = new TextInput(this, std::to_string(40), "mm/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
|
||||
start_vol_sizer->Add(start_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -588,7 +588,7 @@ VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
// end vol
|
||||
auto end_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_vol_text = new wxStaticText(this, wxID_ANY, end_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiEnd = new TextInput(this, std::to_string(200), _L("mm/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiEnd = new TextInput(this, std::to_string(200), "mm/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_vol_sizer->Add(end_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
end_vol_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -597,7 +597,7 @@ VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
// vol step
|
||||
auto vol_step_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto vol_step_text = new wxStaticText(this, wxID_ANY, vol_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(10), _L("mm/s"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(10), "mm/s", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
vol_step_sizer->Add(vol_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
vol_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -680,7 +680,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater
|
||||
// start length
|
||||
auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStart = new TextInput(this, std::to_string(0), _L("mm"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStart = new TextInput(this, std::to_string(0), "mm", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
|
||||
start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -690,7 +690,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater
|
||||
// end length
|
||||
auto end_length_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiEnd = new TextInput(this, std::to_string(2), _L("mm"), "", wxDefaultPosition, ti_size);
|
||||
m_tiEnd = new TextInput(this, std::to_string(2), "mm", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
end_length_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -699,7 +699,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater
|
||||
// length step
|
||||
auto length_step_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto length_step_text = new wxStaticText(this, wxID_ANY, length_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(0.1), _L("mm"), "", wxDefaultPosition, ti_size);
|
||||
m_tiStep = new TextInput(this, wxString::FromDouble(0.1), "mm", "", wxDefaultPosition, ti_size);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
length_step_sizer->Add(length_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
length_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -1043,7 +1043,7 @@ Junction_Deviation_Test_Dlg::Junction_Deviation_Test_Dlg(wxWindow* parent, wxWin
|
||||
// Start junction deviation
|
||||
auto start_jd_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto start_jd_text = new wxStaticText(this, wxID_ANY, start_jd_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiJDStart = new TextInput(this, wxString::Format("%.3f", 0.000), _L("mm"), "", wxDefaultPosition, ti_size);
|
||||
m_tiJDStart = new TextInput(this, wxString::Format("%.3f", 0.000), "mm", "", wxDefaultPosition, ti_size);
|
||||
m_tiJDStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
start_jd_sizer->Add(start_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
start_jd_sizer->Add(m_tiJDStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
@@ -1052,7 +1052,7 @@ Junction_Deviation_Test_Dlg::Junction_Deviation_Test_Dlg(wxWindow* parent, wxWin
|
||||
// End junction deviation
|
||||
auto end_jd_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_jd_text = new wxStaticText(this, wxID_ANY, end_jd_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiJDEnd = new TextInput(this, wxString::Format("%.3f", 0.250), _L("mm"), "", wxDefaultPosition, ti_size);
|
||||
m_tiJDEnd = new TextInput(this, wxString::Format("%.3f", 0.250), "mm", "", wxDefaultPosition, ti_size);
|
||||
m_tiJDEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_jd_sizer->Add(end_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
end_jd_sizer->Add(m_tiJDEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
|
||||
Reference in New Issue
Block a user