Merge branch 'main' into feature/h2c_support_clean

This commit is contained in:
Rodrigo Faselli authored and GitHub committed 2026-07-12 22:00:40 -03:00
commit 79aa0bd4a2
65 files changed
+3958 -1183

No files matched your search

+22 -51
View File
@@ -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
+31 -8
View File
@@ -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 &params, 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);
+13 -10
View File
@@ -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);
}
}
+3
View File
@@ -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;
+4
View File
@@ -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;
-17
View File
@@ -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)
{
-7
View File
@@ -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;
+3 -1
View File
@@ -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",
+13 -13
View File
@@ -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));
+28
View File
@@ -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,
+66 -5
View File
@@ -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;
+3 -1
View File
@@ -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>")
+5 -5
View File
@@ -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);
}
+26 -22
View File
@@ -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();
+14 -12
View File
@@ -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);
+4 -4
View File
@@ -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"})
+10 -6
View File
@@ -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
View File
@@ -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));
+9 -1
View File
@@ -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
+1 -2
View File
@@ -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>>;
+4
View File
@@ -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)) {
+1 -1
View File
@@ -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))
+3 -1
View File
@@ -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))
+23 -15
View File
@@ -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
View File
@@ -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);
+13 -11
View File
@@ -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);
+1
View File
@@ -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)
+1 -1
View File
@@ -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);
+37 -55
View File
@@ -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)
{
+4 -4
View File
@@ -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
+22 -6
View File
@@ -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));