mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 22:18:30 +00:00
Merge branch 'main' into feature/h2c_support_clean
This commit is contained in:
65 files changed
+3958
-1183
No files matched your search
+22
-51
@@ -1335,7 +1335,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill;
|
||||
bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills &&
|
||||
(
|
||||
is_centered_infill ||
|
||||
is_separable_infill_pattern(surface_fill.params.pattern) ||
|
||||
params.config->solid_infill_rotate_template != "" ||
|
||||
params.config->sparse_infill_rotate_template != "" );
|
||||
|
||||
@@ -1364,59 +1364,30 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
|
||||
// Orca: separate infill / per-model pattern centering.
|
||||
//
|
||||
// First assign this fill region to the model part whose slice at this layer overlaps it
|
||||
// the most. A strict "contains" test is ambiguous for assemblies whose parts overlap (a
|
||||
// region may sit inside several parts, or straddle a boundary and be inside none), so we
|
||||
// pick by intersection area instead.
|
||||
//
|
||||
// The center must belong to an *overlap group*, not a single part: parts that
|
||||
// touch/overlap form one connected physical body that shares a single center, while a
|
||||
// part detached from the rest of the assembly gets its own. This holds for both
|
||||
// separated infills and Each_Model surface centering (Each_Model == per connected body).
|
||||
// firstLayerObjGroups() already holds these connected components, so we widen the chosen
|
||||
// part's bbox to the whole group it belongs to.
|
||||
// Center the pattern on each connected body of the object independently, so every piece
|
||||
// is filled exactly as if it were sliced on its own: touching/overlapping parts merge
|
||||
// into one body sharing a center, while separate parts and disconnected islands (even
|
||||
// interleaved-but-not-touching ones, e.g. chain links) each get their own. The body each
|
||||
// island belongs to, and its full bounding box, were resolved in 3D by PrintObject::
|
||||
// infill() (lslices_separated_component_bboxes, aligned with this layer's lslices). We
|
||||
// match this fill region to the island it overlaps most, then re-use the whole-object
|
||||
// bounding box (origin-centered — identical extent to the default, so coverage and cost
|
||||
// are unchanged) re-centered on that body.
|
||||
if (is_per_model_center || is_separate_infill) {
|
||||
double best_overlap = 0.;
|
||||
ObjectID best_vol_id;
|
||||
const PrintInstance* best_instance = nullptr;
|
||||
for (const auto& instance : this->object()->instances()) {
|
||||
for (const auto& volume : instance.print_object->firstLayerObjSlice()) {
|
||||
if (f->layer_id >= volume.slices.size())
|
||||
continue;
|
||||
const double overlap = area(intersection_ex(volume.slices[f->layer_id], ExPolygons{expoly}));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
best_vol_id = volume.volume_id;
|
||||
best_instance = &instance;
|
||||
}
|
||||
double best_overlap = 0.;
|
||||
BoundingBox best_component;
|
||||
for (size_t r = 0; r < this->lslices.size() && r < this->lslices_separated_component_bboxes.size(); ++ r) {
|
||||
const double overlap = area(intersection_ex(this->lslices[r], expoly));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
best_component = this->lslices_separated_component_bboxes[r];
|
||||
}
|
||||
}
|
||||
if (best_instance) {
|
||||
const Transform3d matrix = best_instance->model_instance->get_matrix();
|
||||
Point shift = best_instance->shift; // get_volume_bbox takes a non-const ref
|
||||
auto& volumes = best_instance->model_instance->get_object()->volumes;
|
||||
|
||||
// Volume ids to center on: the whole overlap group the winning part belongs to,
|
||||
// falling back to just that part if it isn't part of any group.
|
||||
std::vector<ObjectID> center_ids;
|
||||
for (const auto& group : best_instance->print_object->firstLayerObjGroups()) {
|
||||
bool in_group = false;
|
||||
for (const ObjectID& vid : group.volume_ids)
|
||||
if (vid == best_vol_id) { in_group = true; break; }
|
||||
if (in_group) { center_ids = group.volume_ids; break; }
|
||||
}
|
||||
if (center_ids.empty())
|
||||
center_ids.push_back(best_vol_id);
|
||||
|
||||
BoundingBox bbox;
|
||||
for (const ObjectID& vid : center_ids)
|
||||
for (auto model_volume : volumes)
|
||||
if (vid.id == model_volume->id().id) {
|
||||
bbox.merge(model_volume->get_volume_bbox(matrix, shift, true));
|
||||
break;
|
||||
}
|
||||
if (bbox.defined)
|
||||
f->set_bounding_box(bbox);
|
||||
if (best_component.defined) {
|
||||
const Point c = best_component.center();
|
||||
BoundingBox part_bbox = bbox; // origin-centered, whole-object extent (from above)
|
||||
part_bbox.translate(c.x(), c.y()); // re-center on this body
|
||||
f->set_bounding_box(part_bbox);
|
||||
}
|
||||
} // - End: separate infill / per-model pattern centering
|
||||
|
||||
|
||||
@@ -2739,13 +2739,19 @@ 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 {
|
||||
// Build the extension around the box center. The transpose merge and the sqrt(2.) scaling
|
||||
// (which covers any possible rotation) are both defined about the origin, so a box that is not
|
||||
// origin-centered — e.g. a separated-infill box re-centered on a single assembly part — would be
|
||||
// distorted. Shift to the origin first and back afterwards; for the default origin-centered box
|
||||
// the two translations cancel and this is identical to the original behavior.
|
||||
const Point c = this->bounding_box.center();
|
||||
BoundingBox out = this->bounding_box;
|
||||
out.translate(-c.x(), -c.y());
|
||||
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.));
|
||||
out = out.scaled(sqrt(2.));
|
||||
out.translate(c.x(), c.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
|
||||
@@ -3098,8 +3104,11 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
|
||||
const coord_t d2 = coord_t(0.5 * period - d1);
|
||||
|
||||
// Align bounding box to the grid
|
||||
bb.merge(align_to_grid(bb.min, Point(period, period)));
|
||||
// Align bounding box to the grid, phased through the box center so separated infills align
|
||||
// each part on itself (grid_center is the origin for a standalone object / feature off).
|
||||
// Captured before the merge, which grows bb and would otherwise shift its center.
|
||||
const Point grid_center = bb.center();
|
||||
bb.merge(align_to_grid(bb.min, Point(period, period), grid_center));
|
||||
const coord_t xmin = bb.min.x();
|
||||
const coord_t xmax = bb.max.x();
|
||||
const coord_t ymin = bb.min.y();
|
||||
@@ -3146,11 +3155,17 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
flip_vertical = !flip_vertical;
|
||||
}
|
||||
|
||||
// transpose points for odd infill layers (taking infill combination into account)
|
||||
// transpose points for odd infill layers (taking infill combination into account).
|
||||
// Orca: mirror across the diagonal through grid_center (not the origin), so the swapped
|
||||
// layers stay aligned with the center-phased grid. For a standalone object / feature off,
|
||||
// grid_center is the origin and this is a plain x/y swap.
|
||||
if (infill_layer_id % 2 == 1) {
|
||||
for (Polyline& pl : polylines) {
|
||||
for (Point& p : pl.points) {
|
||||
std::swap(p.x(), p.y());
|
||||
const coord_t dx = p.x() - grid_center.x();
|
||||
const coord_t dy = p.y() - grid_center.y();
|
||||
p.x() = grid_center.x() + dy;
|
||||
p.y() = grid_center.y() + dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3341,6 +3356,14 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
break;
|
||||
}
|
||||
|
||||
// Orca: cases 1 & 2 build the pattern symmetrically around the origin, so on their own they
|
||||
// phase to the global origin and every part shares one grid. Shift the pattern onto the box
|
||||
// center this->bounding_box carries, so separated infills align each part on itself. The center
|
||||
// is the origin for a standalone object (or when the feature is off), making this a no-op there.
|
||||
if (Pattern_type != 0)
|
||||
for (Polyline &pl : polylines)
|
||||
pl.translate(rotate_vector.second);
|
||||
|
||||
// Apply multiline fill
|
||||
multiline_fill(polylines, params, spacing);
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
|
||||
m_filament_extruders.clear();
|
||||
//ORCA: Reset current extruder ID and clear pointers to prevent dangling pointers when extruders are recreated.
|
||||
m_curr_extruder_id = -1;
|
||||
m_cached_extruder_idx = 0;
|
||||
std::fill(m_curr_filament_extruder.begin(), m_curr_filament_extruder.end(), nullptr);
|
||||
m_filament_extruders.reserve(extruder_ids.size());
|
||||
for (unsigned int extruder_id : extruder_ids)
|
||||
@@ -609,6 +610,7 @@ std::string GCodeWriter::toolchange(unsigned int filament_id, int nozzle_id)
|
||||
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
|
||||
m_curr_extruder_id = filament_extruder_iter->extruder_id();
|
||||
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
|
||||
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
|
||||
|
||||
// return the toolchange command
|
||||
// if we are running a single-extruder setup, just set the extruder and return nothing
|
||||
@@ -676,7 +678,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xy(point_on_plate);
|
||||
auto speed = m_is_first_layer
|
||||
? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id())) : this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
w.emit_f(speed * 60.0);
|
||||
//BBS
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
@@ -762,7 +764,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
||||
// BBS
|
||||
Vec3d dest_point = point;
|
||||
auto travel_speed =
|
||||
m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id())) : this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
//BBS: a z_hop need to be handle when travel
|
||||
if (std::abs(m_to_lift) > EPSILON) {
|
||||
assert(std::abs(m_lifted) < EPSILON);
|
||||
@@ -859,13 +861,13 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
||||
{
|
||||
//force to move xy first then z after filament change
|
||||
w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y()));
|
||||
w.emit_f(this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id())) * 60.0);
|
||||
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
out_string = w.string() + _travel_to_z(point_on_plate.z(), comment);
|
||||
} else {
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point_on_plate);
|
||||
w.emit_f(this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id())) * 60.0);
|
||||
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
out_string = w.string();
|
||||
}
|
||||
@@ -898,10 +900,10 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
||||
{
|
||||
m_pos(2) = z;
|
||||
|
||||
double speed = this->config.travel_speed_z.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
|
||||
if (speed == 0.) {
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id()))
|
||||
: this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
}
|
||||
|
||||
GCodeG1Formatter w;
|
||||
@@ -915,11 +917,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
||||
std::string GCodeWriter::_spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment)
|
||||
{
|
||||
std::string output;
|
||||
double speed = this->config.travel_speed_z.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
|
||||
|
||||
if (speed == 0.) {
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id()))
|
||||
: this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
}
|
||||
|
||||
if (!this->config.enable_arc_fitting) { // Orca: if arc fitting is disabled, approximate the arc with small linear segments
|
||||
@@ -1282,6 +1284,7 @@ void GCodeWriter::init_extruder(unsigned int filament_id)
|
||||
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
|
||||
m_curr_extruder_id = filament_extruder_iter->extruder_id();
|
||||
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
|
||||
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
GCodeWriter() :
|
||||
multiple_extruders(false), m_curr_filament_extruder(MAXIMUM_EXTRUDER_NUMBER, nullptr),
|
||||
m_curr_extruder_id (-1),
|
||||
m_cached_extruder_idx(0),
|
||||
m_single_extruder_multi_material(false),
|
||||
m_last_acceleration(0), m_max_acceleration(0),m_last_travel_acceleration(0), m_max_travel_acceleration(0),
|
||||
m_last_jerk(0), m_max_jerk_x(0), m_max_jerk_y(0),
|
||||
@@ -140,6 +141,8 @@ public:
|
||||
bool m_single_extruder_multi_material;
|
||||
std::vector<Extruder*> m_curr_filament_extruder;
|
||||
int m_curr_extruder_id;
|
||||
// Motion uses the global/base process variant until a filament becomes active.
|
||||
size_t m_cached_extruder_idx;
|
||||
unsigned int m_last_acceleration;
|
||||
unsigned int m_last_travel_acceleration;
|
||||
std::vector<unsigned int> m_max_travel_acceleration;
|
||||
|
||||
@@ -157,6 +157,10 @@ public:
|
||||
ExPolygons lslices;
|
||||
ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support
|
||||
std::vector<BoundingBox> lslices_bboxes;
|
||||
// Orca: for separated infills / per-model centering. Aligned with lslices: for each island, the
|
||||
// full bounding box of the 3D connected body (across all layers) it belongs to. Populated by
|
||||
// PrintObject::infill() only when the feature is used; empty otherwise.
|
||||
std::vector<BoundingBox> lslices_separated_component_bboxes;
|
||||
|
||||
// BBS
|
||||
ExPolygons loverhangs;
|
||||
|
||||
@@ -2708,23 +2708,6 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
|
||||
return *m_convex_hull.get();
|
||||
}
|
||||
|
||||
// Orca: get volume bbox for separate infill
|
||||
static std::mutex mtx_model;
|
||||
BoundingBox ModelVolume::get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache = false) {
|
||||
std::unique_lock l(mtx_model); // locks function here
|
||||
// Orca: the cache is keyed by the instance transform/shift; a ModelVolume is shared
|
||||
// across instances, so returning the cache blindly would hand back another instance's bbox.
|
||||
if (m_cached_volume_bbox.defined && apply_cache
|
||||
&& matrix.isApprox(m_cached_volume_bbox_matrix)
|
||||
&& shift == m_cached_volume_bbox_shift)
|
||||
return m_cached_volume_bbox;
|
||||
auto hull = get_convex_hull_2d(matrix);
|
||||
hull.translate(-shift);
|
||||
m_cached_volume_bbox_matrix = matrix;
|
||||
m_cached_volume_bbox_shift = shift;
|
||||
return m_cached_volume_bbox = hull.bounding_box().polygon().bounding_box();
|
||||
}
|
||||
|
||||
//BBS: refine the model part names
|
||||
ModelVolumeType ModelVolume::type_from_string(const std::string &s)
|
||||
{
|
||||
|
||||
@@ -923,7 +923,6 @@ public:
|
||||
//Orca: cache clearing procedure to ensure that the shape is positioned accurately when manipulating it
|
||||
void clear_cache() {
|
||||
m_cached_trans_matrix = Transform3d::Identity().inverse(); // get unvelivable matrix
|
||||
m_cached_volume_bbox.reset();
|
||||
m_convex_hull_2d.clear();
|
||||
m_cached_2d_polygon.clear();
|
||||
};
|
||||
@@ -969,9 +968,6 @@ public:
|
||||
// Get count of errors in the mesh
|
||||
int get_repaired_errors_count() const;
|
||||
|
||||
BoundingBox get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache);
|
||||
void reset_volume_bbox() { m_cached_volume_bbox.reset(); };
|
||||
|
||||
// Helpers for loading / storing into AMF / 3MF files.
|
||||
static ModelVolumeType type_from_string(const std::string &s);
|
||||
static std::string type_to_string(const ModelVolumeType t);
|
||||
@@ -1059,9 +1055,6 @@ private:
|
||||
mutable Transform3d m_cached_trans_matrix; //BBS, used for convex_hell_2d acceleration
|
||||
mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration
|
||||
Geometry::Transformation m_transformation;
|
||||
mutable BoundingBox m_cached_volume_bbox; //Orca: used for separated infills
|
||||
mutable Transform3d m_cached_volume_bbox_matrix{Transform3d::Identity()}; //Orca: cache key for m_cached_volume_bbox
|
||||
mutable Point m_cached_volume_bbox_shift{Point(0, 0)}; //Orca: cache key for m_cached_volume_bbox
|
||||
|
||||
//BBS: add convex_hell_2d related logic
|
||||
void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const;
|
||||
|
||||
@@ -210,7 +210,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"chamber_minimal_temperature",
|
||||
"thumbnails",
|
||||
"thumbnails_format",
|
||||
"anisotropic_surfaces", "center_of_surface_pattern", "separated_infills",
|
||||
"anisotropic_surfaces",
|
||||
"center_of_surface_pattern",
|
||||
"separated_infills",
|
||||
"seam_gap",
|
||||
"role_based_wipe_speed",
|
||||
"wipe_speed",
|
||||
|
||||
@@ -1377,7 +1377,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The absolute coordinates + Model rotation: If Align directions to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
@@ -1394,7 +1394,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The absolute coordinates + Model rotation: If Align directions to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
@@ -3273,12 +3273,13 @@ void PrintConfigDef::init_fff_params()
|
||||
def->min = 0;
|
||||
def->max = 100;
|
||||
def->set_default_value(new ConfigOptionPercent(20));
|
||||
|
||||
|
||||
def = this->add("align_infill_direction_to_model", coBool);
|
||||
def->label = L("Align infill direction to model");
|
||||
def->label = L("Align directions to model");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Aligns infill, bridge, ironing and surface fill directions to follow the model's orientation on the build plate.\n"
|
||||
"When enabled, directions rotate with the model to maintain optimal strength characteristics.");
|
||||
def->tooltip = L("Aligns infill, bridge, ironing, and top/bottom surface directions to follow the model's orientation on the build plate.\n"
|
||||
"When enabled, these directions rotate together with the model so the printed features keep their intended orientation "
|
||||
"relative to the part, preserving optimal strength and surface characteristics regardless of how the model is placed.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@@ -7199,13 +7200,12 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("separated_infills", coBool);
|
||||
def->label = L("Separated infills");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Aligns the internal infill pattern of each part independently instead of across the whole object or assembly.\n"
|
||||
"By default, aligned infill patterns share a single origin for the entire object, so the pattern of every "
|
||||
"part is referenced to the same point. When enabled, each connected body is aligned on its own: parts that "
|
||||
"touch or overlap are treated as one body and share an origin, while parts detached from the rest each get "
|
||||
"their own.\n Useful when an assembly groups several distinct objects that should each keep a self-centered infill.\n"
|
||||
"Only affects centered infill patterns (Archimedean Chords, Octagram Spiral) and patterns driven by an "
|
||||
"infill rotation template.");
|
||||
def->tooltip = L("Centers the internal infill of each part on itself, as if it were sliced on its own, instead of on the "
|
||||
"whole assembly. Parts that touch or overlap are treated as one body and share a center; separate parts "
|
||||
"(or distinct 3D objects) each get their own.\n"
|
||||
"Useful when an assembly groups several objects that should each keep a consistent, self-centered infill.\n"
|
||||
"Affects line and grid patterns and rotation-template infills.\n"
|
||||
"Patterns locked to global coordinates (Gyroid, Honeycomb, TPMS, ...) are unaffected.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
|
||||
@@ -118,6 +118,34 @@ enum InfillPattern : int {
|
||||
ipCount,
|
||||
};
|
||||
|
||||
// Orca: Infill patterns whose alignment origin follows the fill bounding box, so the
|
||||
// "separated_infills" option can re-center them per connected body. Patterns evaluated in
|
||||
// absolute/global coordinates (Gyroid, TPMS, Honeycomb, CrossHatch, ...) or that are shape-relative
|
||||
// (Concentric) ignore that bounding box and are therefore excluded.
|
||||
inline bool is_separable_infill_pattern(InfillPattern pattern)
|
||||
{
|
||||
switch (pattern) {
|
||||
case ipRectilinear:
|
||||
case ipAlignedRectilinear:
|
||||
case ipZigZag:
|
||||
case ipCrossZag:
|
||||
case ipLockedZag:
|
||||
case ipGrid:
|
||||
case ipTriangles:
|
||||
case ipStars: // tri-hexagon
|
||||
case ipCubic:
|
||||
case ipQuarterCubic:
|
||||
case ipLateralHoneycomb:
|
||||
case ipLateralLattice:
|
||||
case ipHilbertCurve:
|
||||
case ipArchimedeanChords:
|
||||
case ipOctagramSpiral:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum class IroningType {
|
||||
NoIroning,
|
||||
TopSurfaces,
|
||||
|
||||
@@ -563,11 +563,6 @@ void PrintObject::prepare_infill()
|
||||
{
|
||||
if (! this->set_started(posPrepareInfill))
|
||||
return;
|
||||
|
||||
// Orca: clear all volume bbox caches
|
||||
for (auto volume : this->model_object()->volumes)
|
||||
volume->reset_volume_bbox();
|
||||
|
||||
m_print->set_status(25, L("Generating infill regions"));
|
||||
if (m_typed_slices) {
|
||||
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
|
||||
@@ -710,6 +705,72 @@ void PrintObject::infill()
|
||||
|
||||
if (this->set_started(posInfill)) {
|
||||
m_print->set_status(35, L("Generating infill toolpath"));
|
||||
|
||||
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
|
||||
// centering. Two islands belong to the same body when their slices overlap on adjacent
|
||||
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
|
||||
// chain links) stay separate, matching "split to objects". Each layer island then records
|
||||
// the full bounding box of its body, so its infill is centered on that body as if it were
|
||||
// sliced alone. Done once here, before the parallel fill, and only when a region needs it.
|
||||
bool needs_separated_components = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &rc = this->printing_region(i).config();
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
|
||||
needs_separated_components = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fast path: the feature only changes anything when the object is made of more than one
|
||||
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
|
||||
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
|
||||
// body already shares the object center, i.e. the default, so skip the connectivity pass.
|
||||
if (needs_separated_components) {
|
||||
int parts = 0;
|
||||
const ModelVolume *first_part = nullptr;
|
||||
for (const ModelVolume *v : this->model_object()->volumes)
|
||||
if (v->is_model_part()) { ++ parts; first_part = v; }
|
||||
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
|
||||
needs_separated_components = false;
|
||||
}
|
||||
for (Layer *layer : m_layers)
|
||||
layer->lslices_separated_component_bboxes.clear();
|
||||
if (needs_separated_components) {
|
||||
const size_t nl = m_layers.size();
|
||||
std::vector<size_t> offset(nl + 1, 0); // flat index of the first island of each layer
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
|
||||
const size_t nreg = offset[nl];
|
||||
// Union-find over every (layer, island).
|
||||
std::vector<size_t> parent(nreg);
|
||||
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
|
||||
auto find = [&parent](size_t x) {
|
||||
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
|
||||
return x;
|
||||
};
|
||||
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
|
||||
// Join islands that overlap between two consecutive layers.
|
||||
for (size_t i = 0; i + 1 < nl; ++ i) {
|
||||
const Layer *la = m_layers[i], *lb = m_layers[i + 1];
|
||||
for (size_t a = 0; a < la->lslices.size(); ++ a)
|
||||
for (size_t b = 0; b < lb->lslices.size(); ++ b)
|
||||
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
|
||||
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
|
||||
unite(offset[i] + a, offset[i + 1] + b);
|
||||
}
|
||||
// Full bounding box of each body, indexed by its union-find root.
|
||||
std::vector<BoundingBox> body_bbox(nreg);
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
|
||||
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
|
||||
// Store the body bbox for every island.
|
||||
for (size_t i = 0; i < nl; ++ i) {
|
||||
Layer *layer = m_layers[i];
|
||||
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a)
|
||||
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
|
||||
}
|
||||
}
|
||||
|
||||
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
|
||||
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
|
||||
|
||||
|
||||
@@ -324,10 +324,12 @@ AboutDialog::AboutDialog()
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
m_html->SetMinSize(wxSize(FromDIP(-1), FromDIP(16)));
|
||||
m_html->SetBorders(2);
|
||||
wxColour bgr_clr = GetBackgroundColour();
|
||||
const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()));
|
||||
const auto text = from_u8(
|
||||
(boost::format(
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<body bgcolor= \"" + bgr_clr_str + "\" >"
|
||||
"<p style=\"text-align:left\"><a style=\"color:#009789\" href=\"https://www.orcaslicer.com\">https://www.orcaslicer.com</ a></p>"
|
||||
"</body>"
|
||||
"</html>")
|
||||
|
||||
@@ -673,7 +673,7 @@ void MaterialSyncItem::doRender(wxDC &dc)
|
||||
dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 1, MATERIAL_ITEM_SIZE.y - 1, 5);
|
||||
|
||||
if (m_selected) {
|
||||
dc.SetPen(wxColour(0x00, 0xAE, 0x42));
|
||||
dc.SetPen(wxColour("#009688")); // ORCA selected item border color
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 1, MATERIAL_ITEM_SIZE.y - 1, 5);
|
||||
}
|
||||
@@ -684,7 +684,7 @@ void MaterialSyncItem::doRender(wxDC &dc)
|
||||
dc.DrawRoundedRectangle(0, 0, MATERIAL_ITEM_SIZE.x, MATERIAL_ITEM_SIZE.y, 5);
|
||||
|
||||
if (m_selected) {
|
||||
dc.SetPen(wxPen(wxColour(0x00, 0xAE, 0x42), FromDIP(2)));
|
||||
dc.SetPen(wxPen(wxColour("#009688"), FromDIP(2))); // ORCA selected item border color
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5);
|
||||
}
|
||||
@@ -936,7 +936,7 @@ void AmsMapingPopup::msw_rescale()
|
||||
m_split_left_line->SetMaxSize(wxSize(-1, 1));
|
||||
sizer_split_ams->Add(0, 0, 0, wxEXPAND, 0);
|
||||
sizer_split_ams->Add(ams_title_text, 0, wxALIGN_CENTER, 0);
|
||||
sizer_split_ams->Add(m_split_left_line, 1, wxEXPAND, 0);
|
||||
sizer_split_ams->Add(m_split_left_line, 1, wxALIGN_CENTER, 0); // ORCA align line vertically
|
||||
return sizer_split_ams;
|
||||
}
|
||||
|
||||
@@ -1621,7 +1621,7 @@ bool AmsMapingPopup::ProcessLeftDown(wxMouseEvent &event)
|
||||
void AmsMapingPopup::paintEvent(wxPaintEvent &evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
|
||||
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#009688")),FromDIP(2))); // ORCA use colorful border for separation
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
|
||||
}
|
||||
@@ -2482,7 +2482,7 @@ AmsRMGroup* AmsReplaceMaterialDialog::create_backup_group(wxString gname, std::m
|
||||
void AmsReplaceMaterialDialog::paintEvent(wxPaintEvent& evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
|
||||
dc.SetPen(StateColor::darkModeColorFor(wxColour("#DBDBDB"))); // ORCA fix popup border color for dark mode
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,19 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
// SetBackgroundStyle(wxBackgroundStyle::wxBG_STYLE_TRANSPARENT);
|
||||
SetTransparent(m_init_transparent);
|
||||
SetBackgroundColour(wxColour(23, 25, 22, 128));
|
||||
|
||||
// ORCA add border
|
||||
Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#009688")), FromDIP(2)));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
|
||||
});
|
||||
|
||||
int window_padding = 15;
|
||||
auto imgsize = 32;
|
||||
auto imgright = 10;
|
||||
|
||||
//Adaptive Frame Width
|
||||
wxClientDC dc(parent);
|
||||
wxSize msg_sz = dc.GetMultiLineTextExtent(ok_text);
|
||||
@@ -44,21 +57,16 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
|
||||
m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *text_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
text_sizer->AddSpacer(FromDIP(20));
|
||||
auto image_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto imgsize = FromDIP(25);
|
||||
auto completedimg = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("completed", this, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0);
|
||||
image_sizer->Add(completedimg, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(0));
|
||||
image_sizer->AddStretchSpacer();
|
||||
text_sizer->Add(image_sizer);
|
||||
text_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
auto completedimg = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("completed", this, imgsize), wxDefaultPosition, FromDIP(wxSize(imgsize, imgsize)), 0);
|
||||
|
||||
text_sizer->Add(completedimg, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(imgright));
|
||||
m_finish_text = new Label(this, win_text, LB_AUTO_WRAP);
|
||||
m_finish_text->SetMinSize(wxSize(FromDIP(win_width - 64), -1));
|
||||
m_finish_text->SetMaxSize(wxSize(FromDIP(win_width - 64), -1));
|
||||
m_finish_text->SetMinSize(wxSize(FromDIP(win_width - (window_padding * 2 + imgright + imgsize)), -1));
|
||||
m_finish_text->SetMaxSize(wxSize(FromDIP(win_width - (window_padding * 2 + imgright + imgsize)), -1));
|
||||
m_finish_text->SetForegroundColour(wxColour(255, 255, 255, 255));
|
||||
text_sizer->Add(m_finish_text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 0);
|
||||
text_sizer->AddSpacer(FromDIP(20));
|
||||
m_sizer_main->Add(text_sizer, FromDIP(0), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxTOP, FromDIP(15));
|
||||
text_sizer->Add(m_finish_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_sizer_main->Add(text_sizer, 0, wxALL, FromDIP(15));
|
||||
|
||||
wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
bSizer_button->SetMinSize(wxSize(FromDIP(100), -1));
|
||||
@@ -66,22 +74,18 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT);*/
|
||||
bSizer_button->AddStretchSpacer(1);
|
||||
m_button_ok = new Button(this, ok_text);
|
||||
m_button_ok->SetStyle(ButtonStyle::Confirm, ButtonType::Window);
|
||||
m_button_ok->SetSize(wxSize(FromDIP(60), FromDIP(30)));
|
||||
m_button_ok->SetMinSize(wxSize(FromDIP(90), FromDIP(30)));
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
m_button_ok->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(10));
|
||||
|
||||
m_button_ok->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { deal_ok(); });
|
||||
|
||||
m_button_cancel = new Button(this, cancel_text);
|
||||
m_button_cancel->SetStyle(ButtonStyle::Regular, ButtonType::Window);
|
||||
m_button_cancel->SetSize(wxSize(FromDIP(65), FromDIP(30)));
|
||||
m_button_cancel->SetMinSize(wxSize(FromDIP(65), FromDIP(30)));
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
m_button_cancel->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(10));
|
||||
|
||||
m_button_cancel->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { deal_cancel(); });
|
||||
|
||||
m_sizer_main->Add(bSizer_button, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(20));
|
||||
m_sizer_main->Add(bSizer_button, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(window_padding));
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](auto &e) {
|
||||
on_hide();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/timer.h>
|
||||
@@ -20,6 +19,8 @@
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -63,30 +64,30 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||
, timer_state(0)
|
||||
, tech(tech)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
list->SetMinSize(wxSize(80 * em, 30 * em));
|
||||
|
||||
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
label->SetFont(Label::Body_14);
|
||||
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||
|
||||
list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||
list->SetSingleStyle(wxLC_SORT_DESCENDING);
|
||||
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 5 * em);
|
||||
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 20 * em);
|
||||
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 10 * em);
|
||||
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 20 * em);
|
||||
if (tech == ptFFF) {
|
||||
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 5 * em);
|
||||
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 15 * em);
|
||||
}
|
||||
|
||||
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
|
||||
|
||||
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(new wxButton(this, wxID_OK, _L("OK")), 0, wxALL, em);
|
||||
button_sizer->Add(new wxButton(this, wxID_CANCEL, _L("Cancel")), 0, wxALL, em);
|
||||
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
|
||||
auto dlg_btns = new GUI::DialogButtons(this, {"OK", "Cancel"});
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
vsizer->Add(dlg_btns, 0, wxEXPAND);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
|
||||
@@ -241,12 +242,15 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std
|
||||
, m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER))
|
||||
, m_selected_index (selected_index)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
m_list->SetMinSize(wxSize(40 * em, 30 * em));
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname));
|
||||
label->SetFont(Label::Body_14);
|
||||
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||
|
||||
m_list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||
@@ -259,11 +263,9 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std
|
||||
|
||||
vsizer->Add(m_list, 1, wxEXPAND | wxALL, em);
|
||||
|
||||
wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(new wxButton(this, wxID_OK, _L("OK")), 0, wxALL, em);
|
||||
button_sizer->Add(new wxButton(this, wxID_CANCEL, _L("Cancel")), 0, wxALL, em);
|
||||
auto dlg_btns = new GUI::DialogButtons(this, {"OK", "Cancel"});
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
vsizer->Add(dlg_btns, 0, wxEXPAND);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
GUI::wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
@@ -734,10 +734,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line("anisotropic_surfaces", has_centered_surface);
|
||||
|
||||
// Orca: separate infills
|
||||
bool is_internal_infill_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value) ||
|
||||
config->opt_string("sparse_infill_rotate_template") != "" ||
|
||||
config->opt_string("solid_infill_rotate_template") != "";
|
||||
toggle_line("separated_infills", is_internal_infill_centered);
|
||||
bool is_internal_infill_separable = is_separable_infill_pattern(config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value) ||
|
||||
config->opt_string("sparse_infill_rotate_template") != "" ||
|
||||
config->opt_string("solid_infill_rotate_template") != "";
|
||||
toggle_line("separated_infills", is_internal_infill_separable);
|
||||
|
||||
// Orca: no need gaps
|
||||
for (auto el : {"gap_fill_target", "filter_out_gap_fill"})
|
||||
|
||||
@@ -489,7 +489,7 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
|
||||
const float main_row_h = 2.0f * text_h + item_spacing_y; // Two lines of text (position and detail) + spacing between them
|
||||
const float properties_h = static_cast<float>(properties_rows.size()) * (text_h + 2.0f * cell_pad_y) + 2.0f * cell_pad_y + 1.0f + item_spacing_y // table rows
|
||||
+ item_spacing_y + show_button_h // Spacing() + Show/Hide button row
|
||||
+ item_spacing_y + 1.0f + style.FramePadding.y; // Spacing() + Separator() + Dummy()
|
||||
+ item_spacing_y + 1.0f + style.WindowPadding.y; // Spacing() + Separator() + Dummy()
|
||||
const float folded_window_h = std::ceil(window_pad_h + main_row_h); // Height of the window when properties are hidden, with padding, rounded up for better look
|
||||
const float unfolded_window_h = std::ceil(folded_window_h + properties_h); // Height of the window when properties are shown, with padding, rounded up for better look
|
||||
const float window_h = properties_shown ? unfolded_window_h : folded_window_h; // Final window height depending on whether properties are shown or not
|
||||
@@ -615,9 +615,12 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy({0, style.FramePadding.y});
|
||||
ImGui::Dummy({0, style.WindowPadding.y});
|
||||
}
|
||||
|
||||
float draw_area_height = ImGui::GetTextLineHeight() * 2.f + style.ItemSpacing.y;
|
||||
ImGui::Dummy({10.f, draw_area_height}); // reserve area
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding , 3.f * m_scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding , ImVec2(2.f, 2.f) * m_scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button , ImVec4(0, 0, 0, 0));
|
||||
@@ -625,6 +628,10 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive , ImVec4(84 / 255.f, 84 / 255.f, 90 / 255.f, 1.f));
|
||||
|
||||
const float main_wnd_height = ImGui::GetWindowHeight();
|
||||
const float draw_start_y = main_wnd_height - draw_area_height - style.WindowPadding.y;
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(style.WindowPadding.x, draw_start_y));
|
||||
|
||||
// ORCA use glyph based button for fixing button sizes changing depends on used font size on platform
|
||||
const wchar_t foldIcon = properties_shown ? ImGui::UnfoldButtonIcon : ImGui::FoldButtonIcon;
|
||||
if (imgui.glyph_button(foldIcon, ImVec2(16.f, 16.f) * m_scale)) {
|
||||
@@ -640,10 +647,7 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(!properties_shown)
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - style.FramePadding.y); // aligns button with next group
|
||||
ImGui::SetCursorPos(ImVec2(style.WindowPadding.x + style.ItemSpacing.x + 24.f * m_scale, draw_start_y - 1.f * m_scale));
|
||||
|
||||
ImGui::BeginGroup(); // group contents to make information area more compact
|
||||
|
||||
|
||||
+246
-126
@@ -1231,6 +1231,14 @@ GLCanvas3D::~GLCanvas3D()
|
||||
glsafe(::glDeleteTextures(1, &m_ssao_depth_texture_id));
|
||||
m_ssao_depth_texture_id = 0;
|
||||
}
|
||||
if (m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glDeleteTextures(1, &m_shadow_map_texture_id));
|
||||
m_shadow_map_texture_id = 0;
|
||||
}
|
||||
if (m_shadow_map_fbo != 0) {
|
||||
glsafe(::glDeleteFramebuffers(1, &m_shadow_map_fbo));
|
||||
m_shadow_map_fbo = 0;
|
||||
}
|
||||
m_plate_shadow_mask.reset();
|
||||
}
|
||||
m_plate_shadow_mask_key.clear();
|
||||
@@ -2033,6 +2041,9 @@ void GLCanvas3D::render(bool only_init)
|
||||
|
||||
// draw scene
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
// Invalidate the shadow map each frame; only the View3D path below rebuilds it. This keeps
|
||||
// the Preview / Assemble canvases from sampling a stale map with an outdated light matrix.
|
||||
m_shadow_map_valid = false;
|
||||
_render_background();
|
||||
|
||||
//BBS add partplater rendering logic
|
||||
@@ -2057,7 +2068,8 @@ void GLCanvas3D::render(bool only_init)
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
|
||||
//BBS: add outline logic
|
||||
_render_cast_shadows_on_plate(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
// Depth pass for object-on-object and self shadows; consumed by the gouraud shader below.
|
||||
_render_shadows(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
@@ -7858,9 +7870,8 @@ void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transfo
|
||||
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||
{
|
||||
// Check if shadow rendering is enabled in configuration
|
||||
if (wxGetApp().app_config == nullptr)
|
||||
return;
|
||||
if (!wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE))
|
||||
@@ -7874,54 +7885,181 @@ void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, c
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
// Fixed light direction (pointing downward at an angle)
|
||||
// Drive shadow direction from current view angle: define light in eye-space,
|
||||
// then transform it to world-space with inverse view rotation.
|
||||
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
|
||||
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
|
||||
const Vec3d light_dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
||||
const Vec3d ray_dir = -light_dir_to_light; // Direction of shadow projection
|
||||
|
||||
if (std::abs(ray_dir.z()) < 1e-6)
|
||||
if (OpenGLManager::get_framebuffers_type() == OpenGLManager::EFramebufferType::Arb) {
|
||||
|
||||
// Light direction (same as used in shading and plate shading)
|
||||
const Vec3d light_dir_eye = Vec3d(-0.4574957, 0.4574957, 0.7624929).normalized();
|
||||
const Matrix3d view_rot = view_matrix.matrix().block<3, 3>(0, 0);
|
||||
const Vec3d dir_to_light = (view_rot.transpose() * light_dir_eye).normalized();
|
||||
|
||||
// Bounding box of the printable objects (the shadow casters).
|
||||
BoundingBoxf3 obj_bb;
|
||||
for (const GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
obj_bb.merge(volume->transformed_bounding_box());
|
||||
}
|
||||
if (!obj_bb.defined)
|
||||
return; // no objects to cast shadows
|
||||
|
||||
// Orthographic light-space basis (z points toward the light).
|
||||
const Vec3d up = (std::abs(dir_to_light.z()) > 0.99) ? Vec3d::UnitY() : Vec3d::UnitZ();
|
||||
const Vec3d z_axis = dir_to_light;
|
||||
const Vec3d x_axis = up.cross(z_axis).normalized();
|
||||
const Vec3d y_axis = z_axis.cross(x_axis).normalized();
|
||||
|
||||
// Fit the frustum to the object AABB *and* the object's shadow projected onto the plate
|
||||
// (clamped to the plate footprint). This keeps the map tight/high-res for short shadows
|
||||
// while still covering long shadows at grazing light angles, which previously fell outside
|
||||
// the map and were clipped.
|
||||
const Vec3d ray_dir = -dir_to_light; // direction the shadow travels
|
||||
const BoundingBoxf3 plate_bb = m_bed.build_volume().valid() ? m_bed.build_volume().bounding_volume() : obj_bb;
|
||||
|
||||
Vec3d lmin(DBL_MAX, DBL_MAX, DBL_MAX);
|
||||
Vec3d lmax(-DBL_MAX, -DBL_MAX, -DBL_MAX);
|
||||
auto enclose = [&](const Vec3d& p) {
|
||||
const Vec3d lp(x_axis.dot(p), y_axis.dot(p), z_axis.dot(p));
|
||||
lmin = lmin.cwiseMin(lp);
|
||||
lmax = lmax.cwiseMax(lp);
|
||||
};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const Vec3d corner((i & 1) ? obj_bb.max.x() : obj_bb.min.x(),
|
||||
(i & 2) ? obj_bb.max.y() : obj_bb.min.y(),
|
||||
(i & 4) ? obj_bb.max.z() : obj_bb.min.z());
|
||||
enclose(corner);
|
||||
// Where this corner's shadow lands on z = 0, clamped to the plate so a grazing angle
|
||||
// (t -> infinity) stays bounded.
|
||||
if (ray_dir.z() < -1e-6) {
|
||||
const double t = -corner.z() / ray_dir.z();
|
||||
Vec3d s = corner + t * ray_dir;
|
||||
s.x() = std::min(std::max(s.x(), plate_bb.min.x()), plate_bb.max.x());
|
||||
s.y() = std::min(std::max(s.y(), plate_bb.min.y()), plate_bb.max.y());
|
||||
s.z() = 0.0;
|
||||
enclose(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Light "camera" placed just past the nearest enclosed point, looking toward the scene.
|
||||
const double range = lmax.z() - lmin.z();
|
||||
const double margin = std::max(1.0, 0.05 * range);
|
||||
const double cx = 0.5 * (lmin.x() + lmax.x());
|
||||
const double cy = 0.5 * (lmin.y() + lmax.y());
|
||||
const Vec3d eye = x_axis * cx + y_axis * cy + z_axis * (lmax.z() + margin);
|
||||
|
||||
Matrix4d light_view = Matrix4d::Identity();
|
||||
light_view.block<1, 3>(0, 0) = x_axis.transpose();
|
||||
light_view.block<1, 3>(1, 0) = y_axis.transpose();
|
||||
light_view.block<1, 3>(2, 0) = z_axis.transpose();
|
||||
light_view(0, 3) = -x_axis.dot(eye);
|
||||
light_view(1, 3) = -y_axis.dot(eye);
|
||||
light_view(2, 3) = -z_axis.dot(eye);
|
||||
|
||||
// Ortho fit to the light-space extent (symmetric in X/Y around cx,cy; +2% edge padding).
|
||||
const double halfx = std::max(0.5 * (lmax.x() - lmin.x()), 1.0) * 1.02;
|
||||
const double halfy = std::max(0.5 * (lmax.y() - lmin.y()), 1.0) * 1.02;
|
||||
const double near_z = margin * 0.5;
|
||||
const double far_z = range + margin * 1.5;
|
||||
Matrix4d light_proj = Matrix4d::Identity();
|
||||
light_proj(0, 0) = 1.0 / halfx;
|
||||
light_proj(1, 1) = 1.0 / halfy;
|
||||
light_proj(2, 2) = -2.0 / (far_z - near_z);
|
||||
light_proj(2, 3) = -(far_z + near_z) / (far_z - near_z);
|
||||
|
||||
m_shadow_light_vp = Transform3d(light_proj * light_view);
|
||||
|
||||
// Create / resize the depth texture and FBO
|
||||
const unsigned int size = 2048;
|
||||
if (m_shadow_map_texture_id == 0) {
|
||||
glsafe(::glGenTextures(1, &m_shadow_map_texture_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
|
||||
const float border[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glsafe(::glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border));
|
||||
m_shadow_map_size = 0;
|
||||
}
|
||||
if (m_shadow_map_size != size) {
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, size, size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
|
||||
m_shadow_map_size = size;
|
||||
}
|
||||
if (m_shadow_map_fbo == 0)
|
||||
glsafe(::glGenFramebuffers(1, &m_shadow_map_fbo));
|
||||
|
||||
// Save OpenGL state that we will modify
|
||||
GLint prev_viewport[4] = { 0, 0, 0, 0 };
|
||||
glsafe(::glGetIntegerv(GL_VIEWPORT, prev_viewport));
|
||||
GLint prev_fbo = 0;
|
||||
glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo));
|
||||
GLboolean prev_color_mask[4] = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
|
||||
glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, prev_color_mask));
|
||||
const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE);
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, m_shadow_map_fbo));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadow_map_texture_id, 0));
|
||||
glsafe(::glDrawBuffer(GL_NONE));
|
||||
glsafe(::glReadBuffer(GL_NONE));
|
||||
|
||||
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
m_shadow_map_valid = false;
|
||||
} else {
|
||||
glsafe(::glViewport(0, 0, size, size));
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
glsafe(::glDepthFunc(GL_LESS));
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(4.0f, 4.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", Transform3d(light_proj));
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
const Transform3d view_model = Transform3d(light_view) * volume->world_matrix();
|
||||
shader->set_uniform("view_model_matrix", view_model);
|
||||
volume->model.render(shader);
|
||||
}
|
||||
shader->stop_using();
|
||||
|
||||
// Restore state
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glColorMask(prev_color_mask[0], prev_color_mask[1], prev_color_mask[2], prev_color_mask[3]));
|
||||
if (prev_cull)
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
else
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo)));
|
||||
glsafe(::glViewport(prev_viewport[0], prev_viewport[1], prev_viewport[2], prev_viewport[3]));
|
||||
|
||||
m_shadow_map_valid = true;
|
||||
}
|
||||
} else {
|
||||
m_shadow_map_valid = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// Unified plate shadow: draw the build-plate footprint and darken it wherever the same
|
||||
// depth shadow map (built above) says the light is occluded. This replaces the old planar
|
||||
// stencil projection so plate, object and self shadows all come from one technique.
|
||||
// ----------------------------------------------------------------------------------
|
||||
if (!m_shadow_map_valid)
|
||||
return;
|
||||
|
||||
// Shadow projection matrix - flattens geometry onto Z=0 plane along light direction
|
||||
Matrix4d shadow_proj = Matrix4d::Identity();
|
||||
shadow_proj(0, 2) = -ray_dir.x() / ray_dir.z();
|
||||
shadow_proj(1, 2) = -ray_dir.y() / ray_dir.z();
|
||||
shadow_proj(2, 0) = 0.0;
|
||||
shadow_proj(2, 1) = 0.0;
|
||||
shadow_proj(2, 2) = 0.0;
|
||||
shadow_proj(2, 3) = 0.01; // Bias to prevent shadow acne
|
||||
GLShaderProgram* plate_shader = wxGetApp().get_shader("printbed_shadow");
|
||||
if (plate_shader == nullptr)
|
||||
return;
|
||||
|
||||
// Save OpenGL state
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
GLint prev_stencil_mask = 0xFF;
|
||||
glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &prev_stencil_mask));
|
||||
GLboolean prev_stencil_test = GL_FALSE;
|
||||
glsafe(::glGetBooleanv(GL_STENCIL_TEST, &prev_stencil_test));
|
||||
|
||||
// ============================================================
|
||||
// PASS 0: Create stencil mask for the build plate (value = 1)
|
||||
// ============================================================
|
||||
glsafe(::glEnable(GL_STENCIL_TEST));
|
||||
glsafe(::glStencilMask(0xFF));
|
||||
glsafe(::glClearStencil(0));
|
||||
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
||||
|
||||
glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
|
||||
|
||||
glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
|
||||
// Draw the build plate (cached model to avoid per-frame uploads)
|
||||
if (const BuildVolume& build_volume = m_bed.build_volume(); build_volume.valid()) {
|
||||
const std::string mask_key = build_volume.type() == BuildVolume_Type::Rectangle
|
||||
? (boost::format("rect|%1$.5f|%2$.5f|%3$.5f|%4$.5f")
|
||||
@@ -7977,87 +8115,49 @@ void GLCanvas3D::_render_cast_shadows_on_plate(const Transform3d& view_matrix, c
|
||||
}
|
||||
|
||||
if (m_plate_shadow_mask.is_initialized()) {
|
||||
shader->set_uniform("view_model_matrix", view_matrix);
|
||||
m_plate_shadow_mask.render(shader);
|
||||
// Blend the shadow over the already-drawn plate. Depth test keeps it behind anything
|
||||
// already in front; depth writes are off, and a small negative polygon offset lifts it
|
||||
// just above the bed to avoid z-fighting.
|
||||
GLboolean prev_depth_mask = GL_TRUE;
|
||||
glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &prev_depth_mask));
|
||||
GLint prev_depth_func = GL_LESS;
|
||||
glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &prev_depth_func));
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glDepthFunc(GL_LEQUAL));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(-1.0f, -1.0f));
|
||||
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
|
||||
plate_shader->start_using();
|
||||
plate_shader->set_uniform("view_model_matrix", view_matrix);
|
||||
plate_shader->set_uniform("projection_matrix", projection_matrix);
|
||||
plate_shader->set_uniform("shadow_map", 4);
|
||||
plate_shader->set_uniform("shadow_light_vp", m_shadow_light_vp);
|
||||
plate_shader->set_uniform("shadow_intensity", 0.35f);
|
||||
plate_shader->set_uniform("shadow_map_texel", 1.0f / static_cast<float>(m_shadow_map_size));
|
||||
m_plate_shadow_mask.render(plate_shader);
|
||||
plate_shader->stop_using();
|
||||
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 1: Project object shadows onto plate (increment stencil to 2)
|
||||
// ============================================================
|
||||
// Only render where plate exists (stencil == 1), then increment to 2
|
||||
glsafe(::glStencilFunc(GL_EQUAL, 1, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_INCR));
|
||||
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthFunc(GL_ALWAYS)); // Shadows don't need depth testing
|
||||
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glPolygonOffset(-2.0f, -2.0f));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
// Render projected shadow geometry
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if (volume == nullptr || !volume->is_active || !volume->printable || volume->is_modifier || volume->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
// CRITICAL FIX: Apply shadow projection in object's local space, then to world, then to view
|
||||
// This ensures shadows are cast from the object's actual position
|
||||
Matrix4d world_matrix = volume->world_matrix().matrix();
|
||||
|
||||
// Project the shadow - this flattens the geometry onto Z=0 in WORLD space
|
||||
Matrix4d shadow_world_matrix = shadow_proj * world_matrix;
|
||||
|
||||
// Transform to view space for rendering
|
||||
Matrix4d view_shadow_matrix = view_matrix.matrix() * shadow_world_matrix;
|
||||
|
||||
shader->set_uniform("view_model_matrix", view_shadow_matrix);
|
||||
shader->set_uniform("projection_matrix", projection_matrix);
|
||||
|
||||
volume->model.render(shader);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 2: Draw shadow color where stencil == 2
|
||||
// ============================================================
|
||||
glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
|
||||
glsafe(::glStencilFunc(GL_EQUAL, 2, 0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
|
||||
glsafe(::glStencilMask(0x00));
|
||||
|
||||
glsafe(::glDepthFunc(GL_ALWAYS));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
// Draw shadow fill
|
||||
shader->set_uniform("view_model_matrix", Transform3d::Identity());
|
||||
shader->set_uniform("projection_matrix", Transform3d::Identity());
|
||||
|
||||
const ColorRGBA shadow_fill_color(0.0f, 0.0f, 0.0f, 0.4f); // Darker shadow for visibility
|
||||
const ColorRGBA prev_bg_color = m_background.get_geometry().color;
|
||||
m_background.set_color(shadow_fill_color);
|
||||
shader->set_uniform("uniform_color", shadow_fill_color);
|
||||
m_background.render(shader);
|
||||
m_background.set_color(prev_bg_color);
|
||||
shader->set_uniform("uniform_color", prev_bg_color);
|
||||
|
||||
shader->stop_using();
|
||||
|
||||
// ============================================================
|
||||
// RESTORE STATE
|
||||
// ============================================================
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(prev_depth_mask));
|
||||
glsafe(::glDepthFunc(prev_depth_func));
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
|
||||
if (!prev_stencil_test)
|
||||
glsafe(::glDisable(GL_STENCIL_TEST));
|
||||
glsafe(::glStencilMask(prev_stencil_mask));
|
||||
}
|
||||
|
||||
|
||||
void GLCanvas3D::_render_plane() const
|
||||
{
|
||||
;//TODO render assemble plane
|
||||
@@ -8147,6 +8247,20 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
const bool phong_ssao = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SSAO);
|
||||
shader->set_uniform("enable_ssao", phong_ssao);
|
||||
|
||||
// Object-on-object and self shadows: sample the depth map built in _render_shadow_map_pass().
|
||||
// shadow_intensity == 0 disables the effect entirely (unchanged behavior when off / unsupported).
|
||||
if (m_shadow_map_valid && m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_shadow_map_texture_id));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
shader->set_uniform("shadow_map", 4);
|
||||
shader->set_uniform("shadow_light_vp", m_shadow_light_vp);
|
||||
shader->set_uniform("shadow_map_texel", 1.0f / static_cast<float>(m_shadow_map_size));
|
||||
shader->set_uniform("shadow_intensity", 0.35f);
|
||||
}
|
||||
else
|
||||
shader->set_uniform("shadow_intensity", 0.0f);
|
||||
|
||||
const Size& cvn_size = get_canvas_size();
|
||||
{
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
@@ -8238,6 +8352,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
|
||||
shader->set_uniform("show_wireframe", false);
|
||||
}*/
|
||||
|
||||
if (m_shadow_map_valid && m_shadow_map_texture_id != 0) {
|
||||
glsafe(::glActiveTexture(GL_TEXTURE4));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
}
|
||||
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
@@ -9329,7 +9449,7 @@ void GLCanvas3D::_render_canvas_toolbar()
|
||||
);
|
||||
|
||||
create_menu_item( _utf8(L("Realistic View")),
|
||||
true,
|
||||
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
|
||||
cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE),
|
||||
[this, &cfg]{
|
||||
cfg->set_bool(SETTING_OPENGL_REALISTIC_MODE, !cfg->get_bool(SETTING_OPENGL_REALISTIC_MODE));
|
||||
|
||||
@@ -732,6 +732,12 @@ public:
|
||||
std::array<unsigned int, 2> m_ssao_texture_size{ { 0, 0 } };
|
||||
GLModel m_plate_shadow_mask;
|
||||
std::string m_plate_shadow_mask_key;
|
||||
// Depth-based shadow map used to cast object shadows onto other objects and themselves.
|
||||
unsigned int m_shadow_map_fbo{ 0 };
|
||||
unsigned int m_shadow_map_texture_id{ 0 };
|
||||
unsigned int m_shadow_map_size{ 0 };
|
||||
Transform3d m_shadow_light_vp{ Transform3d::Identity() };
|
||||
bool m_shadow_map_valid{ false };
|
||||
public:
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
|
||||
~GLCanvas3D();
|
||||
@@ -1251,7 +1257,9 @@ private:
|
||||
void _render_ssao_pass(unsigned int width, unsigned int height);
|
||||
void _render_background();
|
||||
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
|
||||
void _render_cast_shadows_on_plate(const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
// Build the light-space depth shadow map (consumed by gouraud/phong for object & self shadows)
|
||||
// and cast it onto the build plate. Realistic view only.
|
||||
void _render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix);
|
||||
//BBS: add part plate related logic
|
||||
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true);
|
||||
//BBS: add outline drawing logic
|
||||
|
||||
@@ -457,10 +457,9 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
||||
data.reserve_indices(3 * its.indices.size());
|
||||
|
||||
// Read user preference: smooth normals enabled
|
||||
const bool realistic_mode = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_REALISTIC_MODE);
|
||||
const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
|
||||
|
||||
if (realistic_mode && smooth_normals_enabled) {
|
||||
if (smooth_normals_enabled) {
|
||||
// Use per-corner smooth normals (via IGL)
|
||||
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||
using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
|
||||
|
||||
@@ -68,6 +68,10 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||
valid &= append_shader("thumbnail", { prefix + "thumbnail.vs", prefix + "thumbnail.fs"});
|
||||
// used to render printbed
|
||||
valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" });
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
// used to cast the object shadow map onto the build plate (realistic view)
|
||||
valid &= append_shader("printbed_shadow", { prefix + "printbed_shadow.vs", prefix + "printbed_shadow.fs" });
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
valid &= append_shader("hotbed", {prefix + "hotbed.vs", prefix + "hotbed.fs"});
|
||||
// used to render options in gcode preview
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
|
||||
@@ -1951,7 +1951,7 @@ void GLGizmoEmboss::draw_model_type()
|
||||
float minimum_spacing_x = 8.0f;
|
||||
float minimum_offset_x = ImGui::GetCursorPosX() + minimum_spacing_x;
|
||||
float offset_x = std::max(m_gui_cfg->input_offset, minimum_offset_x);
|
||||
ImGui::SameLine(offset_x);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGuiWrapper::push_radio_style(m_parent.get_scale()); // ORCA
|
||||
if (ImGui::RadioButton(_u8L("Join").c_str(), type == part))
|
||||
|
||||
@@ -1989,7 +1989,7 @@ void GLGizmoSVG::draw_mirroring()
|
||||
|
||||
void GLGizmoSVG::draw_model_type()
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
//ImGui::AlignTextToFramePadding();
|
||||
bool is_last_solid_part = m_volume->is_the_only_one_part();
|
||||
std::string title = _u8L("Operation");
|
||||
if (is_last_solid_part) {
|
||||
@@ -2005,6 +2005,8 @@ void GLGizmoSVG::draw_model_type()
|
||||
ModelVolumeType part = ModelVolumeType::MODEL_PART;
|
||||
ModelVolumeType type = m_volume->type();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
//TRN EmbossOperation
|
||||
ImGuiWrapper::push_radio_style(m_parent.get_scale()); //ORCA
|
||||
if (ImGui::RadioButton(_u8L("Join").c_str(), type == part))
|
||||
|
||||
@@ -2809,21 +2809,6 @@ void MainFrame::init_menubar_as_editor()
|
||||
append_submenu(fileMenu, export_menu, wxID_ANY, _L("Export"), "");
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_ANY, _L("Sync Presets"), _L("Pull and apply the latest presets from OrcaCloud"),
|
||||
[this](wxCommandEvent&) {
|
||||
if (!wxGetApp().is_user_login()) {
|
||||
MessageDialog info_dlg(this, _L("You must be logged in to sync presets from cloud."),
|
||||
_L("Sync Presets"), wxOK | wxICON_INFORMATION);
|
||||
info_dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
wxGetApp().restart_sync_user_preset();
|
||||
}, "", nullptr,
|
||||
[this]() {
|
||||
return wxGetApp().is_user_login() && !wxGetApp().app_config->get_stealth_mode();
|
||||
}, this);
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
|
||||
#ifndef __APPLE__
|
||||
append_menu_item(fileMenu, wxID_EXIT, _L("Quit"), wxString::Format(_L("Quit")),
|
||||
@@ -3289,6 +3274,8 @@ void MainFrame::init_menubar_as_editor()
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
|
||||
m_topbar->GetTopMenu()->AppendSeparator();
|
||||
|
||||
append_menu_item(
|
||||
m_topbar->GetTopMenu(), wxID_ANY, _L("Preset Bundle") + "\t", "",
|
||||
[this](wxCommandEvent &) {
|
||||
@@ -3316,6 +3303,8 @@ void MainFrame::init_menubar_as_editor()
|
||||
return wxGetApp().is_user_login() && !wxGetApp().app_config->get_stealth_mode();
|
||||
}, this);
|
||||
|
||||
m_topbar->GetTopMenu()->AppendSeparator();
|
||||
|
||||
//m_topbar->AddDropDownMenuItem(preference_item);
|
||||
//m_topbar->AddDropDownMenuItem(printer_item);
|
||||
//m_topbar->AddDropDownMenuItem(language_item);
|
||||
@@ -3425,6 +3414,25 @@ void MainFrame::init_menubar_as_editor()
|
||||
plater()->get_current_canvas3D()->force_set_focus();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
|
||||
append_menu_item(
|
||||
fileMenu, wxID_ANY, _L("Sync Presets"), _L("Pull and apply the latest presets from OrcaCloud"),
|
||||
[this](wxCommandEvent&) {
|
||||
if (!wxGetApp().is_user_login()) {
|
||||
MessageDialog info_dlg(this, _L("You must be logged in to sync presets from cloud."),
|
||||
_L("Sync Presets"), wxOK | wxICON_INFORMATION);
|
||||
info_dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
if (m_plater)
|
||||
m_plater->get_notification_manager()->push_notification(
|
||||
into_u8(_L("Syncing presets from cloud\u2026")));
|
||||
wxGetApp().restart_sync_user_preset();
|
||||
}, "", nullptr,
|
||||
[this]() {
|
||||
return wxGetApp().is_user_login() && !wxGetApp().app_config->get_stealth_mode();
|
||||
}, this);
|
||||
|
||||
m_menubar->Append(fileMenu, wxString::Format("&%s", _L("File")));
|
||||
if (editMenu)
|
||||
m_menubar->Append(editMenu, wxString::Format("&%s", _L("Edit")));
|
||||
|
||||
+17
-20
@@ -658,6 +658,7 @@ struct Sidebar::priv
|
||||
//wxComboBox * m_comboBox_print_preset;
|
||||
wxStaticLine * m_staticline1;
|
||||
StaticBox* m_panel_filament_title;
|
||||
wxPanel* m_panel_filament_separator;
|
||||
wxStaticText* m_staticText_filament_settings;
|
||||
wxStaticText* m_staticText_filament_count;
|
||||
ScalableButton * m_bpButton_add_filament;
|
||||
@@ -678,6 +679,7 @@ struct Sidebar::priv
|
||||
|
||||
// BBS printer config
|
||||
StaticBox* m_panel_printer_title = nullptr;
|
||||
wxPanel* m_panel_printer_separator = nullptr;
|
||||
ScalableButton* m_printer_icon = nullptr;
|
||||
ScalableButton* m_printer_connect = nullptr;
|
||||
ScalableButton* m_printer_bbl_sync = nullptr;
|
||||
@@ -2404,10 +2406,6 @@ Sidebar::Sidebar(Plater *parent)
|
||||
p->m_panel_printer_title->Layout();
|
||||
|
||||
// 1.2 Add spliters around title bar
|
||||
// add spliter 1
|
||||
//auto spliter_1 = new ::StaticLine(p->scrolled);
|
||||
//spliter_1->SetBackgroundColour("#A6A9AA");
|
||||
//scrolled_sizer->Add(spliter_1, 0, wxEXPAND);
|
||||
|
||||
// add printer title
|
||||
scrolled_sizer->Add(p->m_panel_printer_title, 0, wxEXPAND | wxALL, 0);
|
||||
@@ -2419,14 +2417,13 @@ Sidebar::Sidebar(Plater *parent)
|
||||
wxString title = _L("Printer") + wxString(!isShown ? "" : (" | " + p->combo_printer->GetValue()));
|
||||
p->m_text_printer_settings->SetLabel(title);
|
||||
p->m_panel_printer_content->Show(!isShown);
|
||||
p->m_panel_printer_separator->Show(isShown);
|
||||
m_scrolled_sizer->Layout();
|
||||
});
|
||||
|
||||
// add spliter 2
|
||||
auto spliter_2 = new ::StaticLine(p->scrolled);
|
||||
spliter_2->SetLineColour("#CECECE");
|
||||
scrolled_sizer->Add(spliter_2, 0, wxEXPAND);
|
||||
|
||||
// ORCA add bottom border for seperation wile sections folded
|
||||
p->m_panel_printer_separator = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(2))); // ORCA staticline class not works without string
|
||||
p->m_panel_printer_separator->SetBackgroundColour("#FFFFFF");
|
||||
scrolled_sizer->Add(p->m_panel_printer_separator, 0, wxEXPAND);
|
||||
|
||||
/*************************** 2. add printer content ************************/
|
||||
|
||||
@@ -2606,9 +2603,9 @@ Sidebar::Sidebar(Plater *parent)
|
||||
}
|
||||
|
||||
wxGridSizer *nozzle_dia_sizer = new wxGridSizer(3, 1, FromDIP(2), 0);
|
||||
nozzle_dia_sizer->Add(p->label_nozzle_title, 0, wxALIGN_CENTER | wxTOP, FromDIP(4));
|
||||
nozzle_dia_sizer->Add(p->combo_nozzle_dia , 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(2));
|
||||
nozzle_dia_sizer->Add(p->label_nozzle_type , 0, wxALIGN_CENTER);
|
||||
nozzle_dia_sizer->Add(p->label_nozzle_title, 1, wxALIGN_CENTER | wxTOP , FromDIP(2));
|
||||
nozzle_dia_sizer->Add(p->combo_nozzle_dia , 0, wxALIGN_CENTER);
|
||||
nozzle_dia_sizer->Add(p->label_nozzle_type , 1, wxALIGN_CENTER | wxBOTTOM, FromDIP(1));
|
||||
|
||||
p->panel_nozzle_dia->SetSizer(nozzle_dia_sizer);
|
||||
|
||||
@@ -2802,7 +2799,9 @@ Sidebar::Sidebar(Plater *parent)
|
||||
else if (ams_btn->IsShown()) exclude_pt = ams_btn->GetPosition().x;
|
||||
if (e.GetPosition().x > exclude_pt)
|
||||
return;
|
||||
p->m_panel_filament_content->Show(!p->m_panel_filament_content->IsShown());
|
||||
bool isShown = p->m_panel_filament_content->IsShown();
|
||||
p->m_panel_filament_content->Show(!isShown);
|
||||
p->m_panel_filament_separator->Show(isShown);
|
||||
m_scrolled_sizer->Layout();
|
||||
|
||||
CallAfter([this]{update_filaments_counter(true);}); // call after all UI processing done
|
||||
@@ -2822,13 +2821,11 @@ Sidebar::Sidebar(Plater *parent)
|
||||
|
||||
p->m_panel_filament_title->SetSizer( bSizer39 );
|
||||
p->m_panel_filament_title->Layout();
|
||||
auto spliter_1 = new ::StaticLine(p->scrolled);
|
||||
spliter_1->SetLineColour("#A6A9AA");
|
||||
scrolled_sizer->Add(spliter_1, 0, wxEXPAND);
|
||||
scrolled_sizer->Add(p->m_panel_filament_title, 0, wxEXPAND | wxALL, 0);
|
||||
auto spliter_2 = new ::StaticLine(p->scrolled);
|
||||
spliter_2->SetLineColour("#CECECE");
|
||||
scrolled_sizer->Add(spliter_2, 0, wxEXPAND);
|
||||
// ORCA add bottom border for seperation wile sections folded
|
||||
p->m_panel_filament_separator = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(2))); // ORCA staticline class not works without string
|
||||
p->m_panel_filament_separator->SetBackgroundColour("#FFFFFF");
|
||||
scrolled_sizer->Add(p->m_panel_filament_separator, 0, wxEXPAND);
|
||||
|
||||
bSizer39->AddStretchSpacer(1);
|
||||
|
||||
|
||||
@@ -797,7 +797,7 @@ wxBoxSizer *PreferencesDialog::create_item_decimal_input(wxString title, wxStrin
|
||||
input->SetToolTip(tooltip);
|
||||
input->GetTextCtrl()->SetValidator(validator);
|
||||
|
||||
m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
|
||||
m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
auto apply_value = [this, param, input, min, max, decimals]() {
|
||||
auto value = input->GetTextCtrl()->GetValue();
|
||||
@@ -1797,6 +1797,17 @@ void PreferencesDialog::create_items()
|
||||
g_sizer = f_sizers.back();
|
||||
g_sizer->AddGrowableCol(0, 1);
|
||||
|
||||
//// GRAPHICS > General
|
||||
g_sizer->Add(create_item_title(_L("General")), 1, wxEXPAND);
|
||||
|
||||
auto smooth_normals = create_item_checkbox(
|
||||
_L("Smooth normals"),
|
||||
_L("Applies smooth normals to the model.\n\nRequires manual scene reload to take effect "
|
||||
"(right-click on 3D view → \"Reload All\")."),
|
||||
SETTING_OPENGL_PHONG_SMOOTH_NORMALS
|
||||
);
|
||||
g_sizer->Add(smooth_normals);
|
||||
|
||||
//// GRAPHICS > Realistic view
|
||||
g_sizer->Add(create_item_title(_L("Realistic View")), 1, wxEXPAND);
|
||||
|
||||
@@ -1816,20 +1827,11 @@ void PreferencesDialog::create_items()
|
||||
|
||||
auto item_realistic_shadows = create_item_checkbox(
|
||||
_L("Shadows"),
|
||||
_L("Renders cast shadows on the plate in realistic view."),
|
||||
_L("Renders cast shadows on the plate, other objects, and each object onto itself in realistic view."),
|
||||
SETTING_OPENGL_PHONG_BASIC_PLATE_SHADOWS
|
||||
);
|
||||
g_sizer->Add(item_realistic_shadows);
|
||||
|
||||
|
||||
auto item_realistic_smooth_normals = create_item_checkbox(
|
||||
_L("Smooth normals"),
|
||||
_L("Applies smooth normals to the realistic view.\n\nRequires manual scene reload to take effect "
|
||||
"(right-click on 3D view → \"Reload All\")."),
|
||||
SETTING_OPENGL_PHONG_SMOOTH_NORMALS
|
||||
);
|
||||
g_sizer->Add(item_realistic_smooth_normals);
|
||||
|
||||
//// GRAPHICS > Anti-aliasing
|
||||
g_sizer->Add(create_item_title(_L("Anti-aliasing")), 1, wxEXPAND);
|
||||
|
||||
|
||||
@@ -1628,6 +1628,7 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type)
|
||||
// BBS: new layout
|
||||
PresetComboBox(parent, preset_type, wxSize(20 * wxGetApp().em_unit(), 30 * wxGetApp().em_unit() / 10))
|
||||
{
|
||||
GetDropDown().SetUseContentWidth(true,true);
|
||||
}
|
||||
|
||||
void TabPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||
|
||||
@@ -30,7 +30,7 @@ static int _ITEM_WIDTH() { return _scale(30); }
|
||||
#define SLIDER_SCALE_10(val) ((val) / 0.01)
|
||||
#define SLIDER_UNSCALE_10(val) ((val) * 0.01)
|
||||
#define LEFT_RIGHT_PADING FromDIP(20)
|
||||
#define FONT_COLOR wxColour("#262E30")
|
||||
#define FONT_COLOR wxColour("#363636") // label color
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/DialogButtons.hpp"
|
||||
#include "CapsuleButton.hpp"
|
||||
#include "PrePrintChecker.hpp"
|
||||
|
||||
@@ -37,7 +38,7 @@ using namespace Slic3r::GUI;
|
||||
#define SyncAmsInfoDialogWidth FromDIP(675)
|
||||
#define SyncAmsInfoDialogHeightMIN FromDIP(620)
|
||||
#define SyncAmsInfoDialogHeightMIDDLE FromDIP(630)
|
||||
#define SyncAmsInfoDialogHeightMAX FromDIP(660)
|
||||
#define SyncAmsInfoDialogHeightMAX FromDIP(700)
|
||||
#define SyncLabelWidth FromDIP(640)
|
||||
#define SyncAttentionTipWidth FromDIP(550)
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -282,14 +283,14 @@ wxBoxSizer *SyncAmsInfoDialog::create_sizer_thumbnail(wxButton *image_button, bo
|
||||
if (left) {
|
||||
wxBoxSizer *text_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto sync_text = new Label(image_button->GetParent(), _CTX(L_CONTEXT("Original", "Sync_AMS"), "Sync_AMS"));
|
||||
sync_text->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
sync_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
text_sizer->Add(sync_text, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
sizer_thumbnail->Add(sync_text, FromDIP(0), wxALIGN_CENTER | wxALL, FromDIP(4));
|
||||
}
|
||||
else {
|
||||
wxBoxSizer *text_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_after_map_text = new Label(image_button->GetParent(), _L("After mapping"));
|
||||
m_after_map_text->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_after_map_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
text_sizer->Add(m_after_map_text, 0, wxALIGN_CENTER | wxALL, 0);
|
||||
sizer_thumbnail->Add(m_after_map_text, FromDIP(0), wxALIGN_CENTER | wxALL, FromDIP(4));
|
||||
}
|
||||
@@ -655,10 +656,6 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
// font
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
// icon
|
||||
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str();
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
Freeze();
|
||||
SetBackgroundColour(m_colour_def_color);
|
||||
|
||||
@@ -733,7 +730,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
m_colormap_btn->Bind(wxEVT_BUTTON, &SyncAmsInfoDialog::update_when_change_map_mode,this); // update_when_change_map_mode(e.GetSelection());
|
||||
m_override_btn->Bind(wxEVT_BUTTON, &SyncAmsInfoDialog::update_when_change_map_mode,this);
|
||||
|
||||
bSizer->Add(m_mode_combox_sizer, FromDIP(0), wxEXPAND | wxTOP, FromDIP(10));
|
||||
bSizer->Add(m_mode_combox_sizer, FromDIP(0), wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
|
||||
}
|
||||
|
||||
m_basic_panel = new wxPanel(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
@@ -847,8 +844,8 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
|
||||
sizer_advanced_options_title = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto advanced_options_title = new Label(m_scrolledWindow, _L("Advanced Options"));
|
||||
advanced_options_title->SetFont(::Label::Body_13);
|
||||
advanced_options_title->SetForegroundColour(wxColour(38, 46, 48));
|
||||
advanced_options_title->SetFont(::Label::Head_14); // ORCA
|
||||
advanced_options_title->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
|
||||
sizer_advanced_options_title->Add(0, 0, 1, wxEXPAND, 0);
|
||||
sizer_advanced_options_title->Add(advanced_options_title, 0, wxALIGN_CENTER, 0);
|
||||
@@ -914,13 +911,15 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
{//new content//tip confirm ok button
|
||||
wxBoxSizer *tip_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_attention_text = new wxStaticText(m_scrolledWindow, wxID_ANY, _L("Tip") + ": ");
|
||||
m_attention_text->SetFont(::Label::Head_14);
|
||||
m_attention_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#009688"))); // ORCA match label colors
|
||||
tip_sizer->Add(m_attention_text, 0, wxALIGN_LEFT | wxTOP, FromDIP(2));
|
||||
m_tip_attention_color_map = _L("Only synchronize filament type and color, not including AMS slot information.");
|
||||
m_tip_attention_override = _L("Replace the project filaments list sequentially based on printer filaments. And unused printer filaments will be automatically added to the end of the list.");
|
||||
m_tip_text = new Label(m_scrolledWindow, m_tip_attention_color_map, LB_AUTO_WRAP);
|
||||
m_tip_text->SetMinSize(wxSize(SyncAttentionTipWidth, -1));
|
||||
m_tip_text->SetMaxSize(wxSize(SyncAttentionTipWidth, -1));
|
||||
m_tip_text->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_tip_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
tip_sizer->Add(m_tip_text, 0, wxALIGN_LEFT | wxTOP, FromDIP(2));
|
||||
tip_sizer->AddSpacer(FromDIP(20));
|
||||
bSizer->Add(tip_sizer, 0, wxEXPAND | wxLEFT, FromDIP(25));
|
||||
@@ -931,7 +930,8 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
|
||||
m_advace_setting_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_more_setting_tips = new wxStaticText(m_scrolledWindow, wxID_ANY, _L("Advanced settings"));
|
||||
m_more_setting_tips->SetForegroundColour(wxColour(0, 137, 123));
|
||||
m_more_setting_tips->SetFont(::Label::Head_14); // ORCA
|
||||
m_more_setting_tips->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_more_setting_tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
|
||||
m_expand_more_settings = !m_expand_more_settings;
|
||||
update_more_setting(true,true);
|
||||
@@ -959,6 +959,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
m_append_color_sizer->Add(m_append_color_checkbox, 0, wxALIGN_LEFT | wxTOP, FromDIP(4));
|
||||
const int gap_between_checebox_and_text = 2;
|
||||
m_append_color_text = new Label(m_scrolledWindow, _L("Add unused AMS filaments to filaments list."));
|
||||
m_append_color_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_append_color_text->Hide();
|
||||
m_append_color_sizer->AddSpacer(FromDIP(gap_between_checebox_and_text));
|
||||
m_append_color_sizer->Add(m_append_color_text, 0, wxALIGN_LEFT | wxTOP, FromDIP(4));
|
||||
@@ -981,6 +982,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
|
||||
|
||||
m_merge_color_text = new Label(m_scrolledWindow, _L("Automatically merge the same colors in the model after mapping."));
|
||||
m_merge_color_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_merge_color_text->Hide();
|
||||
m_merge_color_sizer->AddSpacer(FromDIP(gap_between_checebox_and_text));
|
||||
m_merge_color_sizer->Add(m_merge_color_text, 0, wxALIGN_LEFT | wxTOP, FromDIP(2));
|
||||
@@ -994,17 +996,19 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
m_override_undone_str = _L("After being synced, this action cannot be undone.");
|
||||
m_undone_str = _L("After being synced, the project's filament presets and colors will be replaced with the mapped filament types and colors. This action cannot be undone.");
|
||||
m_confirm_title = new Label(m_scrolledWindow, m_undone_str, LB_AUTO_WRAP);
|
||||
m_confirm_title->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_confirm_title->SetMinSize(wxSize(SyncLabelWidth, -1));
|
||||
m_confirm_title->SetMaxSize(wxSize(SyncLabelWidth, -1));
|
||||
confirm_boxsizer->Add(m_confirm_title, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxTOP | wxRIGHT, FromDIP(10));
|
||||
confirm_boxsizer->Add(m_confirm_title, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxTOP | wxRIGHT | wxBOTTOM, FromDIP(10));
|
||||
m_are_you_sure_title = new wxStaticText(m_scrolledWindow, wxID_ANY, _L("Are you sure to synchronize the filaments?"));
|
||||
m_are_you_sure_title->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
//m_are_you_sure_title->SetFont(Label::Head_14);
|
||||
confirm_boxsizer->Add(m_are_you_sure_title, 0, wxALIGN_LEFT | wxTOP, FromDIP(0));
|
||||
bSizer->Add(confirm_boxsizer, 0, wxALIGN_LEFT | wxLEFT , FromDIP(25));
|
||||
|
||||
wxBoxSizer *warning_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_warning_text = new wxStaticText(m_scrolledWindow, wxID_ANY, _L("Error") + ":");
|
||||
m_warning_text->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_warning_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_warning_text->Hide();
|
||||
warning_sizer->Add(m_warning_text, 0, wxALIGN_CENTER | wxTOP, FromDIP(2));
|
||||
bSizer->Add(warning_sizer, 0, wxEXPAND | wxLEFT, FromDIP(25));
|
||||
@@ -1012,46 +1016,22 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
m_scrolledWindow->SetSizerAndFit(m_sizer_main);
|
||||
m_sizer_show_page->Add(m_scrolledWindow, 1, wxEXPAND, 0);
|
||||
|
||||
wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
bSizer_button->SetMinSize(wxSize(FromDIP(100), -1));
|
||||
/* m_checkbox = new wxCheckBox(this, wxID_ANY, _L("Don't show again"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT);*/
|
||||
bSizer_button->AddStretchSpacer(1);
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed), std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal));
|
||||
m_button_ok = new Button(m_show_page, _L("Synchronize now"));
|
||||
m_button_ok->SetBackgroundColor(btn_bg_green);
|
||||
m_button_ok->SetBorderColor(*wxWHITE);
|
||||
m_button_ok->SetTextColor(wxColour("#FFFFFE"));
|
||||
m_button_ok->SetFont(Label::Body_12);
|
||||
m_button_ok->SetSize(OK_BUTTON_SIZE);
|
||||
m_button_ok->SetMinSize(OK_BUTTON_SIZE);
|
||||
m_button_ok->SetCornerRadius(FromDIP(12));
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
|
||||
auto* dlg_btns = new DialogButtons(m_show_page, {"OK", "Cancel"});
|
||||
m_button_ok = dlg_btns->GetOK();
|
||||
m_button_ok->SetLabel(_L("Synchronize now"));
|
||||
m_button_ok->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) {
|
||||
deal_ok();
|
||||
EndModal(wxID_YES);
|
||||
SetFocusIgnoringChildren();
|
||||
});
|
||||
|
||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
|
||||
m_button_cancel = new Button(m_show_page, m_input_info.cancel_text_to_later ? _L("Later") : _L("Cancel"));
|
||||
m_button_cancel->SetBackgroundColor(btn_bg_white);
|
||||
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
|
||||
m_button_cancel->SetFont(Label::Body_12);
|
||||
m_button_cancel->SetSize(CANCEL_BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(CANCEL_BUTTON_SIZE);
|
||||
m_button_cancel->SetCornerRadius(FromDIP(12));
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
|
||||
m_button_cancel = dlg_btns->GetCANCEL();
|
||||
m_button_cancel->SetLabel(m_input_info.cancel_text_to_later ? _L("Later") : _L("Cancel"));
|
||||
m_button_cancel->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) {
|
||||
EndModal(wxID_CANCEL);
|
||||
});
|
||||
|
||||
m_sizer_show_page->Add(bSizer_button, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(20));
|
||||
m_sizer_show_page->Add(dlg_btns, 0, wxEXPAND);
|
||||
m_show_page->SetSizer(m_sizer_show_page);
|
||||
}
|
||||
show_print_failed_info(false);
|
||||
@@ -2484,10 +2464,10 @@ void SyncAmsInfoDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
}
|
||||
m_swipe_left_button->msw_rescale();
|
||||
m_swipe_right_button->msw_rescale();
|
||||
m_button_ok->SetMinSize(OK_BUTTON_SIZE);
|
||||
m_button_ok->SetCornerRadius(FromDIP(12));
|
||||
m_button_cancel->SetMinSize(CANCEL_BUTTON_SIZE);
|
||||
m_button_cancel->SetCornerRadius(FromDIP(12));
|
||||
//m_button_ok->SetMinSize(OK_BUTTON_SIZE); // ORCA DialogButtons automatically manages style and size
|
||||
//m_button_ok->SetCornerRadius(FromDIP(12));
|
||||
//m_button_cancel->SetMinSize(CANCEL_BUTTON_SIZE);
|
||||
//m_button_cancel->SetCornerRadius(FromDIP(12));
|
||||
m_merge_color_checkbox->Rescale();
|
||||
m_append_color_checkbox->Rescale();
|
||||
m_combobox_plate->Rescale();
|
||||
@@ -2632,14 +2612,14 @@ void SyncAmsInfoDialog::reset_and_sync_ams_list()
|
||||
is_first_row = false;
|
||||
if (!m_original_in_colormap) {
|
||||
m_original_in_colormap = new wxStaticText(m_filament_panel, wxID_ANY, _CTX(L_CONTEXT("Original", "Sync_AMS"), "Sync_AMS") + ":");
|
||||
m_original_in_colormap->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_original_in_colormap->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_original_in_colormap->SetFont(::Label::Head_12);
|
||||
}
|
||||
ams_tip_sizer->Add(m_original_in_colormap, 0, wxALIGN_LEFT | wxTOP, FromDIP(6));
|
||||
|
||||
if (!m_ams_or_ext_text_in_colormap) {
|
||||
m_ams_or_ext_text_in_colormap = new wxStaticText(m_filament_panel, wxID_ANY, _L("AMS") + ":");
|
||||
m_ams_or_ext_text_in_colormap->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_ams_or_ext_text_in_colormap->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_ams_or_ext_text_in_colormap->SetFont(::Label::Head_12);
|
||||
}
|
||||
ams_tip_sizer->Add(m_ams_or_ext_text_in_colormap, 0, wxALIGN_LEFT | wxTOP, FromDIP(9));
|
||||
@@ -2844,7 +2824,7 @@ void SyncAmsInfoDialog::generate_override_fix_ams_list()
|
||||
is_first_row = false;
|
||||
if (!m_original_in_override) {
|
||||
m_original_in_override = new wxStaticText(m_fix_filament_panel, wxID_ANY, _CTX(L_CONTEXT("Original", "Sync_AMS"), "Sync_AMS") + ":");
|
||||
m_original_in_override->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_original_in_override->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_original_in_override->SetFont(::Label::Head_12);
|
||||
}
|
||||
ams_tip_sizer->Add(m_original_in_override, 0, wxALIGN_LEFT | wxTOP, FromDIP(6));
|
||||
@@ -2852,7 +2832,7 @@ void SyncAmsInfoDialog::generate_override_fix_ams_list()
|
||||
if (!m_ams_or_ext_text_in_override) {
|
||||
auto text = (m_only_exist_ext_spool_flag ? _L("Ext spool") : _L("AMS")) + ":";
|
||||
m_ams_or_ext_text_in_override = new wxStaticText(m_fix_filament_panel, wxID_ANY, text);
|
||||
m_ams_or_ext_text_in_override->SetForegroundColour(wxColour(107, 107, 107, 100));
|
||||
m_ams_or_ext_text_in_override->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); // ORCA match label colors
|
||||
m_ams_or_ext_text_in_override->SetFont(::Label::Head_12);
|
||||
}
|
||||
ams_tip_sizer->Add(m_ams_or_ext_text_in_override, 0, wxALIGN_LEFT | wxTOP, FromDIP(9));
|
||||
@@ -3240,10 +3220,12 @@ SyncNozzleAndAmsDialog::SyncNozzleAndAmsDialog(InputInfo &input_info)
|
||||
wxGetApp().preset_bundle->get_printer_extruder_count() == 1 ? 370 :320,
|
||||
input_info.dialog_pos,
|
||||
90,
|
||||
(
|
||||
wxGetApp().preset_bundle->get_printer_extruder_count() == 1 ? _L("Successfully synchronized nozzle information.") :
|
||||
_L("Successfully synchronized nozzle and AMS number information."),
|
||||
_L("Continue to sync filaments"),
|
||||
_CTX(L_CONTEXT("Cancel", "Sync_Nozzle_AMS"), "Sync_Nozzle_AMS"),
|
||||
_L("Successfully synchronized nozzle and AMS number information.")
|
||||
) + "\n\n" + _L("Do you want to continue to sync filaments?"),
|
||||
_L("Yes"), // ORCA use shorter text for buttons
|
||||
_CTX(L_CONTEXT(_L("No"), "Sync_Nozzle_AMS"), "Sync_Nozzle_AMS"),
|
||||
DisappearanceMode::TimedDisappearance)
|
||||
, m_input_info(input_info)
|
||||
{
|
||||
|
||||
@@ -2678,7 +2678,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("ironing_angle", "quality_settings_ironing#angle-offset");
|
||||
optgroup->append_single_option_line("ironing_angle_fixed", "quality_settings_ironing#fixed-angle");
|
||||
|
||||
optgroup = page->new_optgroup(L("Z contouring"), L"param_advanced");
|
||||
optgroup = page->new_optgroup(L("Z contouring"), L"param_z_contouring");
|
||||
optgroup->append_single_option_line("zaa_enabled", "quality_settings_z_contouring");
|
||||
optgroup->append_single_option_line("zaa_minimize_perimeter_height", "quality_settings_z_contouring#minimize-wall-height-angle");
|
||||
optgroup->append_single_option_line("zaa_min_z", "quality_settings_z_contouring#minimum-z-height");
|
||||
@@ -2762,7 +2762,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("top_shell_thickness", "strength_settings_top_bottom_shells#shell-thickness");
|
||||
optgroup->append_single_option_line("top_surface_density", "strength_settings_top_bottom_shells#surface-density");
|
||||
optgroup->append_single_option_line("top_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern");
|
||||
optgroup->append_single_option_line("top_layer_direction", "strength_settings_infill#top-direction");
|
||||
optgroup->append_single_option_line("top_layer_direction", "strength_settings_infill#top-bottom-direction");
|
||||
optgroup->append_single_option_line("top_surface_expansion", "strength_settings_top_bottom_shells#surface-expansion");
|
||||
optgroup->append_single_option_line("top_surface_expansion_margin", "strength_settings_top_bottom_shells#surface-expansion-margin");
|
||||
optgroup->append_single_option_line("top_surface_expansion_direction", "strength_settings_top_bottom_shells#surface-expansion-direction");
|
||||
@@ -2770,7 +2770,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("bottom_shell_thickness", "strength_settings_top_bottom_shells#shell-thickness");
|
||||
optgroup->append_single_option_line("bottom_surface_density", "strength_settings_top_bottom_shells#surface-density");
|
||||
optgroup->append_single_option_line("bottom_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern");
|
||||
optgroup->append_single_option_line("bottom_layer_direction", "strength_settings_infill#direction");
|
||||
optgroup->append_single_option_line("bottom_layer_direction", "strength_settings_infill#top-bottom-direction");
|
||||
optgroup->append_single_option_line("center_of_surface_pattern", "strength_settings_top_bottom_shells#center-surface-pattern-on");
|
||||
optgroup->append_single_option_line("anisotropic_surfaces", "strength_settings_top_bottom_shells#anisotropic-surfaces");
|
||||
optgroup->append_single_option_line("top_bottom_infill_wall_overlap", "strength_settings_top_bottom_shells#infillwall-overlap");
|
||||
@@ -2807,7 +2807,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("infill_wall_overlap", "strength_settings_infill#infill-wall-overlap");
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
||||
optgroup->append_single_option_line("align_infill_direction_to_model", "strength_settings_advanced#align-infill-direction-to-model");
|
||||
optgroup->append_single_option_line("align_infill_direction_to_model", "strength_settings_advanced#align-directions-to-model");
|
||||
optgroup->append_single_option_line("extra_solid_infills", "strength_settings_infill#extra-solid-infill");
|
||||
optgroup->append_single_option_line("bridge_angle", "strength_settings_advanced#bridge-infill-direction");
|
||||
optgroup->append_single_option_line("internal_bridge_angle", "strength_settings_advanced#bridge-infill-direction"); // ORCA: Internal bridge angle override
|
||||
|
||||
@@ -1471,12 +1471,24 @@ FlowRateCalibrationDialog::FlowRateCalibrationDialog(wxWindow* parent, wxWindowI
|
||||
type_box->Add(m_rbType, 0, wxALL | wxEXPAND, FromDIP(4));
|
||||
v_sizer->Add(type_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
|
||||
|
||||
// Pattern selection
|
||||
auto labeled_box_pattern = new LabeledStaticBox(this, _L("Top Surface Pattern"));
|
||||
auto pattern_box = new wxStaticBoxSizer(labeled_box_pattern, wxVERTICAL);
|
||||
// Settings
|
||||
auto stb = new LabeledStaticBox(this, _L("Settings"));
|
||||
auto settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
|
||||
|
||||
wxString pattern_str = _L("Top Surface Pattern");
|
||||
int text_max = GetTextMax(this, std::vector<wxString>{pattern_str});
|
||||
|
||||
settings_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
auto st_size = wxSize(text_max, -1);
|
||||
auto ti_size = FromDIP(wxSize(120, -1));
|
||||
|
||||
// Top surface pattern
|
||||
auto pattern_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto pattern_text = new wxStaticText(this, wxID_ANY, pattern_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
|
||||
// ORCA: Use ComboBox with icons instead of RadioGroup
|
||||
m_rbPattern = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
|
||||
m_rbPattern = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, ti_size, 0, nullptr, wxCB_READONLY);
|
||||
|
||||
boost::filesystem::path image_path(Slic3r::resources_dir());
|
||||
image_path /= "images";
|
||||
@@ -1497,9 +1509,13 @@ FlowRateCalibrationDialog::FlowRateCalibrationDialog(wxWindow* parent, wxWindowI
|
||||
m_rbPattern->SetSelection(0); // Default to Archimedean Chords
|
||||
// ORCA: explicit set value to ensure display on Windows
|
||||
m_rbPattern->SetValue(m_rbPattern->GetString(0));
|
||||
m_rbPattern->GetDropDown().SetUseContentWidth(true);
|
||||
|
||||
pattern_box->Add(m_rbPattern, 0, wxALL | wxEXPAND, FromDIP(4));
|
||||
v_sizer->Add(pattern_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
|
||||
pattern_sizer->Add(pattern_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
pattern_sizer->Add(m_rbPattern , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
settings_sizer->Add(pattern_sizer, 0, wxLEFT, FromDIP(3));
|
||||
|
||||
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
|
||||
|
||||
v_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
|
||||
Reference in new issue
Block a user