Merge upstream main: center of mass markers, adaptive TPMS, texture bake color mixing, post-processing preview fix, GLEW/OpenCSG removal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Clifford Garwood
2026-10-10 01:44:42 -04:00
co-authored by Claude Opus 5.5
128 changed files with 5541 additions and 67999 deletions
+152
View File
@@ -24,6 +24,7 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
#include "PrintBase.hpp"
#include "ConnectedBodies.hpp"
#include "PrintConfig.hpp"
#include "enum_bitmask.hpp"
#include "libslic3r.h"
@@ -2563,6 +2564,156 @@ WipeTowerType GCode::wipe_tower_type()
return WipeTowerType::Type2;
}
// Numbers the object instances and the connected bodies of the instances of several, for the processor to find those an
// extrusion lies in.
static void set_mass_locator(GCodeProcessor &processor, const Print &print)
{
struct Object
{
const PrintObject *object;
int first_instance;
// No bodies for an object of one.
size_t bodies_count;
int first_body;
std::vector<coordf_t> print_zs;
// Per layer, the body of each island and a locator whose boxes are widened for walls reaching past them.
std::vector<std::vector<size_t>> bodies;
std::vector<IslandLocator> islands;
// Per instance, whether its widened box reaches another's, so that the box of an island proves nothing.
std::vector<bool> crowded;
};
std::vector<Object> objects;
std::vector<GCodeProcessorResult::ObjectMass> object_masses;
int bodies_total = 0;
for (const PrintObject *object : print.objects()) {
const auto layers = object->layers();
if (layers.empty())
continue;
// Bodies for assemblies only, as the Prepare tab counts them: those separated infills found, if it needed them.
const ModelVolumePtrs &volumes = object->model_object()->volumes;
const bool assembly = std::count_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return v->is_model_part(); }) > 1 ||
std::any_of(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return v->is_negative_volume(); });
size_t count = 0;
std::vector<std::vector<size_t>> bodies;
if (assembly) {
count = object->separated_body_bboxes().size();
if (count > 0 && std::all_of(layers.begin(), layers.end(), [](const Layer *l) { return l->lslices_separated_component_ids.size() == l->lslices.size(); }))
for (const Layer *layer : layers)
bodies.emplace_back(layer->lslices_separated_component_ids);
else {
std::vector<const ExPolygons *> islands;
for (const Layer *layer : layers)
islands.emplace_back(&layer->lslices);
bodies = connected_bodies(islands, count);
}
}
if (count < 2) {
count = 0;
bodies.assign(layers.size(), {});
}
Object &o = objects.emplace_back(Object{ object, int(object_masses.size()), count, bodies_total, {}, std::move(bodies), {}, {} });
object_masses.resize(object_masses.size() + object->instances().size());
for (size_t instance = 0; instance < object->instances().size(); ++instance)
object_masses[o.first_instance + instance].assembly = assembly;
bodies_total += int(count * object->instances().size());
for (const Layer *layer : layers) {
o.print_zs.emplace_back(layer->print_z);
o.islands.emplace_back(layer->lslices, scaled<coord_t>(1.));
}
}
if (objects.empty())
return;
std::vector<BoundingBox> boxes;
for (const Object &o : objects) {
BoundingBox box;
for (const IslandLocator &islands : o.islands)
for (const BoundingBox &island : islands.boxes())
box.merge(island);
for (const PrintInstance &instance : o.object->instances()) {
BoundingBox &moved = boxes.emplace_back(box);
moved.translate(instance.shift);
}
}
for (Object &o : objects)
for (size_t instance = 0; instance < o.object->instances().size(); ++instance) {
const size_t i = o.first_instance + instance;
o.crowded.emplace_back(false);
for (size_t j = 0; j < boxes.size() && !o.crowded.back(); ++j)
o.crowded.back() = j != i && boxes[i].overlap(boxes[j]);
}
struct Hit
{
size_t object{ 0 }, instance{ 0 }, layer{ 0 }, island{ 0 };
};
auto locate = [objects = std::move(objects), footprints = std::move(boxes),
last = std::optional<Hit>()](const Vec3d &point, bool support) mutable -> GCodeProcessor::MassLocation {
// Supports stand below and around their object: the instance whose footprint holds the point, the one whose center
// is nearest among several, else the nearest footprint.
if (support) {
const Point p(scaled(point.x()), scaled(point.y()));
int found = -1;
bool inside = false;
double best = std::numeric_limits<double>::max();
for (size_t i = 0; i < footprints.size(); ++i) {
const BoundingBox &box = footprints[i];
const double gap = Point((box.min - p).cwiseMax(p - box.max).cwiseMax(0)).cast<double>().squaredNorm();
const bool in = gap == 0.;
const double d = in ? (box.center() - p).cast<double>().squaredNorm() : gap;
if ((in && !inside) || (in == inside && d < best)) {
found = int(i);
inside = in;
best = d;
}
}
return { found, -1 };
}
constexpr double z_tolerance = 0.002;
const auto local = [&point, &objects](size_t object, size_t instance) {
return Point(Point(scaled(point.x()), scaled(point.y())) - objects[object].object->instances()[instance].shift);
};
const auto location = [&objects, &last](const Hit &hit) {
last = hit;
const Object &o = objects[hit.object];
return GCodeProcessor::MassLocation{ o.first_instance + int(hit.instance),
o.bodies_count == 0 ? -1 : o.first_body + int(hit.instance * o.bodies_count + o.bodies[hit.layer][hit.island]) };
};
// A point lies on the first layer at or above it, as spiral vase rises through each layer.
// Extrusions mostly follow each other on one island.
if (last) {
const Object &o = objects[last->object];
if (point.z() <= o.print_zs[last->layer] + z_tolerance &&
(last->layer == 0 || point.z() > o.print_zs[last->layer - 1] + z_tolerance) &&
o.islands[last->layer].holds(last->island, local(last->object, last->instance), o.crowded[last->instance]))
return location(*last);
}
// Outside the islands of instances crowding each other, the nearest outline.
std::optional<Hit> nearest;
double distance = std::numeric_limits<double>::max();
for (size_t object = 0; object < objects.size(); ++object) {
const Object &o = objects[object];
const auto z = std::lower_bound(o.print_zs.begin(), o.print_zs.end(), point.z() - z_tolerance);
if (z == o.print_zs.end())
continue;
const size_t layer = size_t(z - o.print_zs.begin());
for (size_t instance = 0; instance < o.object->instances().size(); ++instance) {
const auto [island, d] = o.islands[layer].find(local(object, instance), o.crowded[instance]);
if (island < 0)
continue;
const Hit hit{ object, instance, layer, size_t(island) };
if (d == 0. || !o.crowded[instance])
return location(hit);
if (d < distance) {
distance = d;
nearest = hit;
}
}
}
return nearest ? location(*nearest) : GCodeProcessor::MassLocation{};
};
processor.set_mass_locator(std::move(locate), std::move(object_masses));
}
void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
{
PROFILE_CLEAR();
@@ -3117,6 +3268,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// modifies m_silent_time_estimator_enabled
DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled,
print.get_layered_nozzle_group_result());
set_mass_locator(m_processor, print);
const bool is_bbl_printers = print.is_BBL_printer();
const bool skip_config_block = print.config().gcode_skip_config_block;
const WipeTowerType wipe_tower_type = print.wipe_tower_type();