Merge remote-tracking branch 'remotes/origin/master' into vb_treesupports
This commit is contained in:
commit
25da414f5c
29 changed files with 603 additions and 179 deletions
|
@ -398,7 +398,7 @@ Vec3d extract_rotation(const Transform3d& transform)
|
|||
#if ENABLE_WORLD_COORDINATE
|
||||
Transform3d Transformation::get_offset_matrix() const
|
||||
{
|
||||
return assemble_transform(get_offset());
|
||||
return translation_transform(get_offset());
|
||||
}
|
||||
|
||||
static Transform3d extract_rotation_matrix(const Transform3d& trafo)
|
||||
|
|
|
@ -436,6 +436,8 @@ public:
|
|||
|
||||
const Vec3d& get_rotation() const { return m_rotation; }
|
||||
double get_rotation(Axis axis) const { return m_rotation(axis); }
|
||||
|
||||
Transform3d get_rotation_matrix() const { return rotation_transform(get_rotation()); }
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
void set_rotation(const Vec3d& rotation);
|
||||
|
@ -454,6 +456,8 @@ public:
|
|||
#else
|
||||
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
|
||||
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
|
||||
|
||||
Transform3d get_scaling_factor_matrix() const { return scale_transform(get_scaling_factor()); }
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// execute fn for each pixel on the line. If fn returns false, terminate the iteration
|
||||
template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
|
||||
{
|
||||
coord_t dx = abs(x1 - x0);
|
||||
|
@ -39,7 +40,7 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
|
|||
dy *= 2;
|
||||
|
||||
for (; n > 0; --n) {
|
||||
fn(x, y);
|
||||
if (!fn(x, y)) return;
|
||||
|
||||
if (error > 0) {
|
||||
x += x_inc;
|
||||
|
@ -55,11 +56,11 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
|
|||
// may call the fn on the same coordiantes multiple times!
|
||||
template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
|
||||
{
|
||||
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
|
||||
normal.x() = ceil(normal.x());
|
||||
normal.y() = ceil(normal.y());
|
||||
Point start_offset = Point(x0,y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1,y1) + (normal).cast<coord_t>();
|
||||
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
|
||||
normal.x() = ceil(normal.x());
|
||||
normal.y() = ceil(normal.y());
|
||||
Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
|
||||
|
||||
dda(x0, y0, x1, y1, fn);
|
||||
dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn);
|
||||
|
@ -95,7 +96,7 @@ private:
|
|||
|
||||
bool is_jump_point(CellPositionType pos, CellPositionType forward_dir) const
|
||||
{
|
||||
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
|
||||
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
|
||||
// diagonal
|
||||
CellPositionType horizontal_check_dir = CellPositionType{forward_dir.x(), 0};
|
||||
CellPositionType vertical_check_dir = CellPositionType{0, forward_dir.y()};
|
||||
|
@ -169,8 +170,11 @@ public:
|
|||
|
||||
float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); }
|
||||
|
||||
size_t unique_id(Node n) const { return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y())); }
|
||||
|
||||
size_t unique_id(Node n) const
|
||||
{
|
||||
return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y()));
|
||||
}
|
||||
|
||||
const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
|
||||
};
|
||||
|
||||
|
@ -189,6 +193,7 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
|
|||
obstacle_min.x() = std::min(obstacle_min.x(), x);
|
||||
obstacle_min.y() = std::min(obstacle_min.y(), y);
|
||||
inpassable.insert(Pixel{x, y});
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const Line &l : obstacles) {
|
||||
|
@ -200,36 +205,13 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
|
|||
|
||||
void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin)
|
||||
{
|
||||
if (layer != nullptr) { this->print_z = layer->print_z; }
|
||||
if (layer == nullptr) return;
|
||||
|
||||
auto store_obstacle = [&](coord_t x, coord_t y) {
|
||||
obstacle_max.x() = std::max(obstacle_max.x(), x);
|
||||
obstacle_max.y() = std::max(obstacle_max.y(), y);
|
||||
obstacle_min.x() = std::min(obstacle_min.x(), x);
|
||||
obstacle_min.y() = std::min(obstacle_min.y(), y);
|
||||
inpassable.insert(Pixel{x, y});
|
||||
};
|
||||
this->print_z = layer->print_z;
|
||||
Lines obstacles;
|
||||
for (size_t step = 0; step < 3; step++) {
|
||||
if (layer != nullptr) {
|
||||
obstacles.insert(obstacles.end(), layer->malformed_lines.begin(), layer->malformed_lines.end());
|
||||
layer = layer->lower_layer;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Line &l : obstacles) {
|
||||
Pixel start = pixelize(l.a + global_origin);
|
||||
Pixel end = pixelize(l.b + global_origin);
|
||||
double_dda_with_offset(start.x(), start.y(), end.x(), end.y(), store_obstacle);
|
||||
}
|
||||
#ifdef DEBUG_FILES
|
||||
::Slic3r::SVG svg(debug_out_path(("obstacles_jps" + std::to_string(print_z) + "_" + std::to_string(rand() % 1000)).c_str()).c_str(),
|
||||
get_extents(obstacles));
|
||||
svg.draw(obstacles);
|
||||
svg.Close();
|
||||
#endif
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
add_obstacles(obstacles);
|
||||
}
|
||||
|
||||
Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
||||
|
@ -238,21 +220,40 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
Pixel end = pixelize(p1);
|
||||
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
|
||||
|
||||
BoundingBox search_box({start,end,obstacle_max,obstacle_min});
|
||||
search_box.max += Pixel(1,1);
|
||||
search_box.min -= Pixel(1,1);
|
||||
if (inpassable.find(start) != inpassable.end()) {
|
||||
dda(start.x(), start.y(), end.x(), end.y(), [&](coord_t x, coord_t y) {
|
||||
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
|
||||
start = Pixel(x, y);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (inpassable.find(end) != inpassable.end()) {
|
||||
dda(end.x(), end.y(), start.x(), start.y(), [&](coord_t x, coord_t y) {
|
||||
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
|
||||
end = Pixel(x, y);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
BoundingBox bounding_square(Points{start,end});
|
||||
bounding_square.max += Pixel(5,5);
|
||||
bounding_square.min -= Pixel(5,5);
|
||||
coord_t bounding_square_size = 2*std::max(bounding_square.size().x(),bounding_square.size().y());
|
||||
BoundingBox search_box({start, end, obstacle_max, obstacle_min});
|
||||
search_box.max += Pixel(1, 1);
|
||||
search_box.min -= Pixel(1, 1);
|
||||
|
||||
BoundingBox bounding_square(Points{start, end});
|
||||
bounding_square.max += Pixel(5, 5);
|
||||
bounding_square.min -= Pixel(5, 5);
|
||||
coord_t bounding_square_size = 2 * std::max(bounding_square.size().x(), bounding_square.size().y());
|
||||
bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2;
|
||||
bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2;
|
||||
bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2;
|
||||
bounding_square.min.y() -= (bounding_square_size - bounding_square.size().y()) / 2;
|
||||
|
||||
// Intersection - limit the search box to a square area around the start and end, to fasten the path searching
|
||||
// Intersection - limit the search box to a square area around the start and end, to fasten the path searching
|
||||
search_box.max = search_box.max.cwiseMin(bounding_square.max);
|
||||
search_box.min = search_box.min.cwiseMax(bounding_square.min);
|
||||
|
||||
|
@ -283,9 +284,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
closest_qnode = astar_cache[closest_qnode].parent;
|
||||
}
|
||||
} else {
|
||||
for (const auto& node : out_nodes) {
|
||||
out_path.push_back(node.position);
|
||||
}
|
||||
for (const auto &node : out_nodes) { out_path.push_back(node.position); }
|
||||
out_path.push_back(start);
|
||||
}
|
||||
|
||||
|
@ -335,7 +334,11 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
|||
if (i - index_of_last_stored_point < 2) continue;
|
||||
bool passable = true;
|
||||
auto store_obstacle = [&](coord_t x, coord_t y) {
|
||||
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) { passable = false; };
|
||||
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) {
|
||||
passable = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle);
|
||||
if (!passable) {
|
||||
|
|
|
@ -1260,7 +1260,7 @@ void ModelObject::apply_cut_connectors(const std::string& new_name)
|
|||
ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
|
||||
|
||||
// Transform the new modifier to be aligned inside the instance
|
||||
new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m *
|
||||
new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m *
|
||||
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
|
||||
|
||||
new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
|
||||
|
@ -1437,7 +1437,7 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d&
|
|||
using namespace Geometry;
|
||||
|
||||
const Transformation cut_transformation = Transformation(cut_matrix);
|
||||
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1 * cut_transformation.get_offset());
|
||||
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1 * cut_transformation.get_offset());
|
||||
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
|
@ -1505,7 +1505,7 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan
|
|||
obj_instance->set_transformation(Transformation());
|
||||
|
||||
const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() :
|
||||
assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace;
|
||||
rotation_transform(obj_instance->get_rotation()) * local_displace;
|
||||
obj_instance->set_offset(offset + displace);
|
||||
|
||||
Vec3d rotation = Vec3d::Zero();
|
||||
|
@ -1560,15 +1560,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
|
|||
// in the transformation matrix and not applied to the mesh transform.
|
||||
|
||||
// const auto instance_matrix = instances[instance]->get_matrix(true);
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const auto instance_matrix = instances[instance]->get_transformation().get_matrix_no_offset();
|
||||
#else
|
||||
const auto instance_matrix = assemble_transform(
|
||||
Vec3d::Zero(), // don't apply offset
|
||||
instances[instance]->get_rotation(),
|
||||
instances[instance]->get_scaling_factor(),
|
||||
instances[instance]->get_mirror()
|
||||
);
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
const Transformation cut_transformation = Transformation(cut_matrix);
|
||||
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1. * cut_transformation.get_offset());
|
||||
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
|
||||
|
||||
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||
Vec3d local_displace = Vec3d::Zero();
|
||||
|
@ -2338,8 +2342,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
|
||||
Vec3d rotation = get_rotation();
|
||||
rotation.z() = 0.;
|
||||
Transform3d trafo_instance =
|
||||
Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
|
||||
Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
|
||||
|
||||
Polygon p = get_object()->convex_hull_2d(trafo_instance);
|
||||
|
||||
|
|
|
@ -549,23 +549,21 @@ namespace boost { namespace polygon {
|
|||
|
||||
// Serialization through the Cereal library
|
||||
namespace cereal {
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); }
|
||||
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
|
||||
|
||||
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
|
||||
template<class Archive> void load(Archive& archive, Slic3r::Transform3d& m) { archive.loadBinary((char*)m.data(), sizeof(double) * 16); }
|
||||
template<class Archive> void save(Archive& archive, const Slic3r::Transform3d& m) { archive.saveBinary((char*)m.data(), sizeof(double) * 16); }
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
}
|
||||
|
||||
// To be able to use Vec<> and Mat<> in range based for loops:
|
||||
|
|
|
@ -58,7 +58,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
// Cache the plenty of parameters, which influence the G-code generator only,
|
||||
// or they are only notes not influencing the generated G-code.
|
||||
static std::unordered_set<std::string> steps_gcode = {
|
||||
"avoid_curled_filament_during_travels",
|
||||
"avoid_crossing_perimeters",
|
||||
"avoid_crossing_perimeters_max_detour",
|
||||
"bed_shape",
|
||||
|
@ -223,6 +222,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
osteps.emplace_back(posInfill);
|
||||
osteps.emplace_back(posSupportMaterial);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (opt_key == "avoid_curled_filament_during_travels") {
|
||||
osteps.emplace_back(posEstimateCurledExtrusions);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
//FIXME invalidate all steps of all objects as well?
|
||||
|
@ -382,6 +383,16 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
|
||||
// which causes that the warning will be showed after arrangement with the
|
||||
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Geometry::Transformation trafo = model_instance0->get_transformation();
|
||||
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
||||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||||
offset(print_object->model_object()->convex_hull_2d(trafo.get_matrix()),
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - BuildVolume::BedEpsilon)),
|
||||
jtRound, scale_(0.1)).front());
|
||||
#else
|
||||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||||
offset(print_object->model_object()->convex_hull_2d(
|
||||
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||
|
@ -389,7 +400,8 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - BuildVolume::BedEpsilon)),
|
||||
jtRound, scale_(0.1)).front());
|
||||
}
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
}
|
||||
// Make a copy, so it may be rotated for instances.
|
||||
Polygon convex_hull0 = it_convex_hull->second;
|
||||
const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
|
||||
|
|
|
@ -300,12 +300,17 @@ struct RotfinderBoilerplate {
|
|||
TriangleMesh mesh = mo.raw_mesh();
|
||||
|
||||
ModelInstance *mi = mo.instances[0];
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const Geometry::Transformation trafo = mi->get_transformation();
|
||||
Transform3d trafo_instance = trafo.get_scaling_factor_matrix() * trafo.get_mirror_matrix();
|
||||
#else
|
||||
auto rotation = Vec3d::Zero();
|
||||
auto offset = Vec3d::Zero();
|
||||
Transform3d trafo_instance =
|
||||
Geometry::assemble_transform(offset, rotation,
|
||||
mi->get_scaling_factor(),
|
||||
mi->get_mirror());
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
|
||||
mesh.transform(trafo_instance);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue