Fixed conflicts after merge with master
This commit is contained in:
commit
2c9d047520
67 changed files with 13605 additions and 9748 deletions
|
@ -4,11 +4,14 @@
|
|||
#include <cmath> // std::isinf() is here
|
||||
#include <unordered_map>
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/MutablePriorityQueue.hpp"
|
||||
|
||||
namespace Slic3r { namespace astar {
|
||||
|
||||
// Borrowed from C++20
|
||||
template<class T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
// Input interface for the Astar algorithm. Specialize this struct for a
|
||||
// particular type and implement all the 4 methods and specify the Node type
|
||||
// to register the new type for the astar implementation.
|
||||
|
|
|
@ -19,6 +19,13 @@ void ExPolygon::scale(double factor)
|
|||
hole.scale(factor);
|
||||
}
|
||||
|
||||
void ExPolygon::scale(double factor_x, double factor_y)
|
||||
{
|
||||
contour.scale(factor_x, factor_y);
|
||||
for (Polygon &hole : holes)
|
||||
hole.scale(factor_x, factor_y);
|
||||
}
|
||||
|
||||
void ExPolygon::translate(const Point &p)
|
||||
{
|
||||
contour.translate(p);
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
|
||||
void clear() { contour.points.clear(); holes.clear(); }
|
||||
void scale(double factor);
|
||||
void scale(double factor_x, double factor_y);
|
||||
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
|
||||
void translate(const Point &vector);
|
||||
void rotate(double angle);
|
||||
|
|
|
@ -77,6 +77,7 @@ const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config
|
|||
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
|
||||
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
|
||||
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
|
||||
const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml";
|
||||
|
||||
static constexpr const char* MODEL_TAG = "model";
|
||||
static constexpr const char* RESOURCES_TAG = "resources";
|
||||
|
@ -408,6 +409,19 @@ namespace Slic3r {
|
|||
VolumeMetadataList volumes;
|
||||
};
|
||||
|
||||
struct CutObjectInfo
|
||||
{
|
||||
struct Connector
|
||||
{
|
||||
int volume_id;
|
||||
int type;
|
||||
float r_tolerance;
|
||||
float h_tolerance;
|
||||
};
|
||||
CutObjectBase id;
|
||||
std::vector<Connector> connectors;
|
||||
};
|
||||
|
||||
// Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
|
||||
typedef std::map<int, int> IdToModelObjectMap;
|
||||
typedef std::map<int, ComponentsList> IdToAliasesMap;
|
||||
|
@ -416,6 +430,7 @@ namespace Slic3r {
|
|||
typedef std::map<int, Geometry> IdToGeometryMap;
|
||||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
|
||||
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
|
||||
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;
|
||||
|
||||
|
@ -443,6 +458,7 @@ namespace Slic3r {
|
|||
IdToGeometryMap m_geometries;
|
||||
CurrentConfig m_curr_config;
|
||||
IdToMetadataMap m_objects_metadata;
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
IdToSlaSupportPointsMap m_sla_support_points;
|
||||
|
@ -474,6 +490,7 @@ namespace Slic3r {
|
|||
|
||||
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
|
||||
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
|
@ -676,6 +693,10 @@ namespace Slic3r {
|
|||
// extract slic3r layer heights profile file
|
||||
_extract_layer_heights_profile_config_from_archive(archive, stat);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) {
|
||||
// extract slic3r layer config ranges file
|
||||
_extract_cut_information_from_archive(archive, stat, config_substitutions);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) {
|
||||
// extract slic3r layer config ranges file
|
||||
_extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
|
||||
|
@ -818,6 +839,19 @@ namespace Slic3r {
|
|||
|
||||
if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr, config_substitutions))
|
||||
return false;
|
||||
|
||||
// Apply cut information for object if any was loaded
|
||||
// m_cut_object_ids are indexed by a 1 based model object index.
|
||||
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
|
||||
if (cut_object_info != m_cut_object_infos.end()) {
|
||||
model_object->cut_id = cut_object_info->second.id;
|
||||
|
||||
for (auto connector : cut_object_info->second.connectors) {
|
||||
assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size()));
|
||||
model_object->volumes[connector.volume_id]->cut_info =
|
||||
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If instances contain a single volume, the volume offset should be 0,0,0
|
||||
|
@ -944,6 +978,65 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
void _3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0) {
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading cut information data to buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream iss(buffer); // wrap returned xml to istringstream
|
||||
pt::ptree objects_tree;
|
||||
pt::read_xml(iss, objects_tree);
|
||||
|
||||
for (const auto& object : objects_tree.get_child("objects")) {
|
||||
pt::ptree object_tree = object.second;
|
||||
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
|
||||
if (obj_idx <= 0) {
|
||||
add_error("Found invalid object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
IdToCutObjectInfoMap::iterator object_item = m_cut_object_infos.find(obj_idx);
|
||||
if (object_item != m_cut_object_infos.end()) {
|
||||
add_error("Found duplicated cut_object_id");
|
||||
continue;
|
||||
}
|
||||
|
||||
CutObjectBase cut_id;
|
||||
std::vector<CutObjectInfo::Connector> connectors;
|
||||
|
||||
for (const auto& obj_cut_info : object_tree) {
|
||||
if (obj_cut_info.first == "cut_id") {
|
||||
pt::ptree cut_id_tree = obj_cut_info.second;
|
||||
cut_id = CutObjectBase(ObjectID( cut_id_tree.get<size_t>("<xmlattr>.id")),
|
||||
cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
|
||||
cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
|
||||
}
|
||||
if (obj_cut_info.first == "connectors") {
|
||||
pt::ptree cut_connectors_tree = obj_cut_info.second;
|
||||
for (const auto& cut_connector : cut_connectors_tree) {
|
||||
if (cut_connector.first != "connector")
|
||||
continue;
|
||||
pt::ptree connector_tree = cut_connector.second;
|
||||
CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"),
|
||||
connector_tree.get<int>("<xmlattr>.type"),
|
||||
connector_tree.get<float>("<xmlattr>.r_tolerance"),
|
||||
connector_tree.get<float>("<xmlattr>.h_tolerance")};
|
||||
connectors.emplace_back(connector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CutObjectInfo cut_info {cut_id, connectors};
|
||||
m_cut_object_infos.insert({ obj_idx, cut_info });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _3MF_Importer::_extract_print_config_from_archive(
|
||||
mz_zip_archive& archive, const mz_zip_archive_file_stat& stat,
|
||||
DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions,
|
||||
|
@ -2219,6 +2312,7 @@ namespace Slic3r {
|
|||
bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
|
||||
bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
|
@ -2281,6 +2375,15 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Adds file with information for object cut ("Metadata/Slic3r_PE_cut_information.txt").
|
||||
// All information for object cut of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
|
||||
// The index differes from the index of an object ID of an object instance of a 3MF file!
|
||||
if (!_add_cut_information_file_to_archive(archive, model)) {
|
||||
close_zip_writer(&archive);
|
||||
boost::filesystem::remove(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
|
||||
// All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
|
||||
// The index differes from the index of an object ID of an object instance of a 3MF file!
|
||||
|
@ -2781,6 +2884,67 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::string out = "";
|
||||
pt::ptree tree;
|
||||
|
||||
unsigned int object_cnt = 0;
|
||||
for (const ModelObject* object : model.objects) {
|
||||
object_cnt++;
|
||||
pt::ptree& obj_tree = tree.add("objects.object", "");
|
||||
|
||||
obj_tree.put("<xmlattr>.id", object_cnt);
|
||||
|
||||
// Store info for cut_id
|
||||
pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
|
||||
|
||||
// store cut_id atributes
|
||||
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
|
||||
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
|
||||
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
|
||||
|
||||
int volume_idx = -1;
|
||||
for (const ModelVolume* volume : object->volumes) {
|
||||
++volume_idx;
|
||||
if (volume->is_cut_connector()) {
|
||||
pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
|
||||
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
|
||||
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
|
||||
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
|
||||
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tree.empty()) {
|
||||
std::ostringstream oss;
|
||||
pt::write_xml(oss, tree);
|
||||
out = oss.str();
|
||||
|
||||
// Post processing("beautification") of the output string for a better preview
|
||||
boost::replace_all(out, "><object", ">\n <object");
|
||||
boost::replace_all(out, "><cut_id", ">\n <cut_id");
|
||||
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
|
||||
boost::replace_all(out, "><connectors", ">\n <connectors");
|
||||
boost::replace_all(out, "></connectors>", ">\n </connectors>");
|
||||
boost::replace_all(out, "><connector", ">\n <connector");
|
||||
boost::replace_all(out, "></connector>", ">\n </connector>");
|
||||
boost::replace_all(out, "></object>", ">\n </object>");
|
||||
// OR just
|
||||
boost::replace_all(out, "><", ">\n<");
|
||||
}
|
||||
|
||||
if (!out.empty()) {
|
||||
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
|
||||
add_error("Unable to add cut information file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
|
|
|
@ -464,12 +464,25 @@ static constexpr const double volume_threshold_inches = 9.0; // 9 = 3*3*3;
|
|||
|
||||
bool Model::looks_like_imperial_units() const
|
||||
{
|
||||
if (this->objects.size() == 0)
|
||||
if (this->objects.empty())
|
||||
return false;
|
||||
|
||||
for (ModelObject* obj : this->objects)
|
||||
if (obj->get_object_stl_stats().volume < volume_threshold_inches)
|
||||
return true;
|
||||
if (obj->get_object_stl_stats().volume < volume_threshold_inches) {
|
||||
if (!obj->is_cut())
|
||||
return true;
|
||||
bool all_cut_parts_look_like_imperial_units = true;
|
||||
for (ModelObject* obj_other : this->objects) {
|
||||
if (obj_other == obj)
|
||||
continue;
|
||||
if (obj_other->cut_id.is_equal(obj->cut_id) && obj_other->get_object_stl_stats().volume >= volume_threshold_inches) {
|
||||
all_cut_parts_look_like_imperial_units = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_cut_parts_look_like_imperial_units)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -613,6 +626,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
this->layer_height_profile = rhs.layer_height_profile;
|
||||
this->printable = rhs.printable;
|
||||
this->origin_translation = rhs.origin_translation;
|
||||
this->cut_id.copy(rhs.cut_id);
|
||||
m_bounding_box = rhs.m_bounding_box;
|
||||
m_bounding_box_valid = rhs.m_bounding_box_valid;
|
||||
m_raw_bounding_box = rhs.m_raw_bounding_box;
|
||||
|
@ -715,6 +729,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t
|
|||
ModelVolume* v = new ModelVolume(this, other);
|
||||
if (type != ModelVolumeType::INVALID && v->type() != type)
|
||||
v->set_type(type);
|
||||
v->cut_info = other.cut_info;
|
||||
this->volumes.push_back(v);
|
||||
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
|
||||
// v->center_geometry_after_creation();
|
||||
|
@ -1189,34 +1204,355 @@ size_t ModelObject::parts_count() const
|
|||
return num;
|
||||
}
|
||||
|
||||
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes)
|
||||
bool ModelObject::has_connectors() const
|
||||
{
|
||||
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
assert(is_cut());
|
||||
for (const ModelVolume* v : this->volumes)
|
||||
if (v->cut_info.is_connector)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
|
||||
{
|
||||
indexed_triangle_set connector_mesh;
|
||||
|
||||
int sectorCount {1};
|
||||
switch (CutConnectorShape(connector_attributes.shape)) {
|
||||
case CutConnectorShape::Triangle:
|
||||
sectorCount = 3;
|
||||
break;
|
||||
case CutConnectorShape::Square:
|
||||
sectorCount = 4;
|
||||
break;
|
||||
case CutConnectorShape::Circle:
|
||||
sectorCount = 360;
|
||||
break;
|
||||
case CutConnectorShape::Hexagon:
|
||||
sectorCount = 6;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (connector_attributes.style == CutConnectorStyle::Prizm)
|
||||
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
|
||||
else if (connector_attributes.type == CutConnectorType::Plug)
|
||||
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
|
||||
else
|
||||
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
|
||||
|
||||
return connector_mesh;
|
||||
}
|
||||
|
||||
void ModelObject::apply_cut_connectors(const std::string& new_name)
|
||||
{
|
||||
if (cut_connectors.empty())
|
||||
return;
|
||||
|
||||
using namespace Geometry;
|
||||
|
||||
size_t connector_id = cut_id.connectors_cnt();
|
||||
for (const CutConnector& connector : cut_connectors) {
|
||||
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
|
||||
// Mesh will be centered when loading.
|
||||
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 *
|
||||
scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast<double>()));
|
||||
|
||||
new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance };
|
||||
new_volume->name = new_name + "-" + std::to_string(++connector_id);
|
||||
}
|
||||
cut_id.increase_connectors_cnt(cut_connectors.size());
|
||||
|
||||
// delete all connectors
|
||||
cut_connectors.clear();
|
||||
}
|
||||
|
||||
void ModelObject::invalidate_cut()
|
||||
{
|
||||
this->cut_id.invalidate();
|
||||
for (ModelVolume* volume : this->volumes)
|
||||
volume->invalidate_cut_info();
|
||||
}
|
||||
|
||||
void ModelObject::synchronize_model_after_cut()
|
||||
{
|
||||
for (ModelObject* obj : m_model->objects) {
|
||||
if (obj == this || obj->cut_id.is_equal(this->cut_id))
|
||||
continue;
|
||||
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
|
||||
obj->cut_id.copy(this->cut_id);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
|
||||
{
|
||||
// we don't save cut information, if result will not contains all parts of initial object
|
||||
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
return;
|
||||
|
||||
if (cut_id.id().invalid())
|
||||
cut_id.init();
|
||||
{
|
||||
int cut_obj_cnt = -1;
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++;
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++;
|
||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt++;
|
||||
if (cut_obj_cnt > 0)
|
||||
cut_id.increase_check_sum(size_t(cut_obj_cnt));
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObject::clone_for_cut(ModelObject** obj)
|
||||
{
|
||||
(*obj) = ModelObject::new_clone(*this);
|
||||
(*obj)->set_model(nullptr);
|
||||
(*obj)->sla_support_points.clear();
|
||||
(*obj)->sla_drain_holes.clear();
|
||||
(*obj)->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
(*obj)->clear_volumes();
|
||||
(*obj)->input_file.clear();
|
||||
}
|
||||
|
||||
void ModelVolume::reset_extra_facets()
|
||||
{
|
||||
this->supported_facets.reset();
|
||||
this->seam_facets.reset();
|
||||
this->mmu_segmentation_facets.reset();
|
||||
}
|
||||
|
||||
void ModelVolume::apply_tolerance()
|
||||
{
|
||||
assert(cut_info.is_connector);
|
||||
if (cut_info.is_processed)
|
||||
return;
|
||||
|
||||
Vec3d sf = get_scaling_factor();
|
||||
/*
|
||||
// correct Z offset in respect to the new size
|
||||
Vec3d pos = vol->get_offset();
|
||||
pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance;
|
||||
vol->set_offset(pos);
|
||||
*/
|
||||
// make a "hole" wider
|
||||
sf[X] *= 1. + double(cut_info.radius_tolerance);
|
||||
sf[Y] *= 1. + double(cut_info.radius_tolerance);
|
||||
|
||||
// make a "hole" dipper
|
||||
sf[Z] *= 1. + double(cut_info.height_tolerance);
|
||||
|
||||
set_scaling_factor(sf);
|
||||
}
|
||||
|
||||
void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
||||
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace)
|
||||
{
|
||||
assert(volume->cut_info.is_connector);
|
||||
volume->cut_info.set_processed();
|
||||
|
||||
const auto volume_matrix = volume->get_matrix();
|
||||
|
||||
// ! Don't apply instance transformation for the conntectors.
|
||||
// This transformation is already there
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
ModelVolume* vol = upper->add_volume(*volume);
|
||||
vol->set_transformation(volume_matrix);
|
||||
vol->apply_tolerance();
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||
ModelVolume* vol = lower->add_volume(*volume);
|
||||
vol->set_transformation(volume_matrix);
|
||||
|
||||
if (volume->cut_info.connector_type == CutConnectorType::Dowel)
|
||||
vol->apply_tolerance();
|
||||
else
|
||||
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
|
||||
vol->set_type(ModelVolumeType::MODEL_PART);
|
||||
}
|
||||
if (volume->cut_info.connector_type == CutConnectorType::Dowel &&
|
||||
attributes.has(ModelObjectCutAttribute::CreateDowels)) {
|
||||
ModelObject* dowel{ nullptr };
|
||||
// Clone the object to duplicate instances, materials etc.
|
||||
clone_for_cut(&dowel);
|
||||
|
||||
// add one more solid part same as connector if this connector is a dowel
|
||||
ModelVolume* vol = dowel->add_volume(*volume);
|
||||
vol->set_type(ModelVolumeType::MODEL_PART);
|
||||
|
||||
// But discard rotation and Z-offset for this volume
|
||||
vol->set_rotation(Vec3d::Zero());
|
||||
vol->set_offset(Z, 0.0);
|
||||
|
||||
// Compute the displacement (in instance coordinates) to be applied to place the dowels
|
||||
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
|
||||
|
||||
dowels.push_back(dowel);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
|
||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
|
||||
{
|
||||
const auto volume_matrix = instance_matrix * volume->get_matrix();
|
||||
|
||||
// Modifiers are not cut, but we still need to add the instance transformation
|
||||
// to the modifier volume transformation to preserve their shape properly.
|
||||
volume->set_transformation(Geometry::Transformation(volume_matrix));
|
||||
|
||||
// Some logic for the negative volumes/connectors. Add only needed modifiers
|
||||
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
|
||||
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
|
||||
upper->add_volume(*volume);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
|
||||
lower->add_volume(*volume);
|
||||
}
|
||||
|
||||
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix)
|
||||
{
|
||||
if (mesh.empty())
|
||||
return;
|
||||
|
||||
mesh.transform(cut_matrix);
|
||||
ModelVolume* vol = object->add_volume(mesh);
|
||||
|
||||
vol->name = src_volume->name;
|
||||
// Don't copy the config's ID.
|
||||
vol->config.assign_config(src_volume->config);
|
||||
assert(vol->config.id().valid());
|
||||
assert(vol->config.id() != src_volume->config.id());
|
||||
vol->set_material(src_volume->material_id(), *src_volume->material());
|
||||
vol->cut_info = src_volume->cut_info;
|
||||
}
|
||||
|
||||
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace)
|
||||
{
|
||||
const auto volume_matrix = volume->get_matrix();
|
||||
|
||||
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());
|
||||
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
|
||||
|
||||
volume->reset_mesh();
|
||||
// Reset volume transformation except for offset
|
||||
const Vec3d offset = volume->get_offset();
|
||||
volume->set_transformation(Geometry::Transformation());
|
||||
volume->set_offset(offset);
|
||||
|
||||
// Perform cut
|
||||
|
||||
TriangleMesh upper_mesh, lower_mesh;
|
||||
{
|
||||
indexed_triangle_set upper_its, lower_its;
|
||||
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
upper_mesh = TriangleMesh(upper_its);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower_mesh = TriangleMesh(lower_its);
|
||||
}
|
||||
|
||||
// Add required cut parts to the objects
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
|
||||
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
|
||||
|
||||
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance)
|
||||
{
|
||||
if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) {
|
||||
object->center_around_origin();
|
||||
object->translate_instances(-object->origin_translation);
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
else {
|
||||
object->invalidate_bounding_box();
|
||||
object->center_around_origin();
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
|
||||
bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero())
|
||||
{
|
||||
using namespace Geometry;
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
|
||||
for (size_t i = 0; i < object->instances.size(); ++i) {
|
||||
auto& obj_instance = object->instances[i];
|
||||
const Vec3d offset = obj_instance->get_offset();
|
||||
const double rot_z = obj_instance->get_rotation().z();
|
||||
|
||||
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;
|
||||
obj_instance->set_offset(offset + displace);
|
||||
|
||||
Vec3d rotation = Vec3d::Zero();
|
||||
if (!flip && !place_on_cut) {
|
||||
if ( i != src_instance_idx)
|
||||
rotation[Z] = rot_z;
|
||||
}
|
||||
else {
|
||||
Transform3d rotation_matrix = Transform3d::Identity();
|
||||
if (flip)
|
||||
rotation_matrix = rotation_transform(PI * Vec3d::UnitX());
|
||||
|
||||
if (place_on_cut)
|
||||
rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse();
|
||||
|
||||
if (i != src_instance_idx)
|
||||
rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix;
|
||||
|
||||
rotation = Transformation(rotation_matrix).get_rotation();
|
||||
}
|
||||
|
||||
obj_instance->set_rotation(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
|
||||
{
|
||||
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
return {};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
|
||||
|
||||
// apply cut attributes for object
|
||||
apply_cut_attributes(attributes);
|
||||
|
||||
// Clone the object to duplicate instances, materials etc.
|
||||
ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr;
|
||||
ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr;
|
||||
ModelObject* upper{ nullptr };
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
clone_for_cut(&upper);
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||
upper->set_model(nullptr);
|
||||
upper->sla_support_points.clear();
|
||||
upper->sla_drain_holes.clear();
|
||||
upper->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
upper->clear_volumes();
|
||||
upper->input_file.clear();
|
||||
}
|
||||
ModelObject* lower{ nullptr };
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
clone_for_cut(&lower);
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||
lower->set_model(nullptr);
|
||||
lower->sla_support_points.clear();
|
||||
lower->sla_drain_holes.clear();
|
||||
lower->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
lower->clear_volumes();
|
||||
lower->input_file.clear();
|
||||
}
|
||||
std::vector<ModelObject*> dowels;
|
||||
|
||||
using namespace Geometry;
|
||||
|
||||
// Because transformations are going to be applied to meshes directly,
|
||||
// we reset transformation of all instances and volumes,
|
||||
|
@ -1224,128 +1560,72 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr
|
|||
// in the transformation matrix and not applied to the mesh transform.
|
||||
|
||||
// const auto instance_matrix = instances[instance]->get_matrix(true);
|
||||
const auto instance_matrix = Geometry::assemble_transform(
|
||||
const auto instance_matrix = assemble_transform(
|
||||
Vec3d::Zero(), // don't apply offset
|
||||
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 0.0)), // don't apply Z-rotation
|
||||
instances[instance]->get_rotation(),
|
||||
instances[instance]->get_scaling_factor(),
|
||||
instances[instance]->get_mirror()
|
||||
);
|
||||
|
||||
z -= instances[instance]->get_offset().z();
|
||||
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());
|
||||
|
||||
// Displacement (in instance coordinates) to be applied to place the upper parts
|
||||
Vec3d local_displace = Vec3d::Zero();
|
||||
Vec3d local_dowels_displace = Vec3d::Zero();
|
||||
|
||||
for (ModelVolume *volume : volumes) {
|
||||
const auto volume_matrix = volume->get_matrix();
|
||||
for (ModelVolume* volume : volumes) {
|
||||
volume->reset_extra_facets();
|
||||
|
||||
volume->supported_facets.reset();
|
||||
volume->seam_facets.reset();
|
||||
volume->mmu_segmentation_facets.reset();
|
||||
|
||||
if (! volume->is_model_part()) {
|
||||
// Modifiers are not cut, but we still need to add the instance transformation
|
||||
// to the modifier volume transformation to preserve their shape properly.
|
||||
|
||||
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
upper->add_volume(*volume);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower->add_volume(*volume);
|
||||
}
|
||||
else if (! volume->mesh().empty()) {
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.transform(instance_matrix * volume_matrix, true);
|
||||
volume->reset_mesh();
|
||||
// Reset volume transformation except for offset
|
||||
const Vec3d offset = volume->get_offset();
|
||||
volume->set_transformation(Geometry::Transformation());
|
||||
volume->set_offset(offset);
|
||||
|
||||
// Perform cut
|
||||
TriangleMesh upper_mesh, lower_mesh;
|
||||
{
|
||||
indexed_triangle_set upper_its, lower_its;
|
||||
cut_mesh(mesh.its, float(z), &upper_its, &lower_its);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
upper_mesh = TriangleMesh(upper_its);
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower))
|
||||
lower_mesh = TriangleMesh(lower_its);
|
||||
}
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) {
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
vol->config.assign_config(volume->config);
|
||||
assert(vol->config.id().valid());
|
||||
assert(vol->config.id() != volume->config.id());
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) {
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
vol->config.assign_config(volume->config);
|
||||
assert(vol->config.id().valid());
|
||||
assert(vol->config.id() != volume->config.id());
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
|
||||
// The upper part displacement is set to half of the lower part bounding box
|
||||
// this is done in hope at least a part of the upper part will always be visible and draggable
|
||||
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
|
||||
}
|
||||
if (!volume->is_model_part()) {
|
||||
if (volume->cut_info.is_processed)
|
||||
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
|
||||
else
|
||||
process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
|
||||
}
|
||||
else if (!volume->mesh().empty())
|
||||
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace);
|
||||
}
|
||||
|
||||
// Post-process cut parts
|
||||
|
||||
ModelObjectPtrs res;
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
|
||||
if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
upper->center_around_origin();
|
||||
upper->translate_instances(-upper->origin_translation);
|
||||
upper->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (size_t i = 0; i < instances.size(); ++i) {
|
||||
auto &instance = upper->instances[i];
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace;
|
||||
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset + displace);
|
||||
instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
||||
invalidate_translations(upper, instances[instance]);
|
||||
|
||||
reset_instance_transformation(upper, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
||||
attributes.has(ModelObjectCutAttribute::FlipUpper),
|
||||
local_displace);
|
||||
res.push_back(upper);
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
|
||||
if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) {
|
||||
lower->center_around_origin();
|
||||
lower->translate_instances(-lower->origin_translation);
|
||||
lower->origin_translation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
// Reset instance transformation except offset and Z-rotation
|
||||
for (auto *instance : lower->instances) {
|
||||
const Vec3d offset = instance->get_offset();
|
||||
const double rot_z = instance->get_rotation().z();
|
||||
instance->set_transformation(Geometry::Transformation());
|
||||
instance->set_offset(offset);
|
||||
instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z));
|
||||
}
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
||||
invalidate_translations(lower, instances[instance]);
|
||||
|
||||
reset_instance_transformation(lower, instance, cut_matrix,
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||
res.push_back(lower);
|
||||
}
|
||||
|
||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
||||
for (auto dowel : dowels) {
|
||||
invalidate_translations(dowel, instances[instance]);
|
||||
|
||||
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
|
||||
|
||||
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
|
||||
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
||||
res.push_back(dowel);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
||||
|
||||
synchronize_model_after_cut();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1381,6 +1661,12 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh));
|
||||
|
||||
// Invalidate extruder value in volume's config,
|
||||
// otherwise there will no way to change extruder for object after splitting,
|
||||
// because volume's extruder value overrides object's extruder value.
|
||||
if (new_vol->config.has("extruder"))
|
||||
new_vol->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances) {
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
Vec3d shift = model_instance->get_transformation().get_matrix_no_offset() * new_vol->get_offset();
|
||||
|
@ -2289,6 +2575,14 @@ bool model_has_multi_part_objects(const Model &model)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool model_has_connectors(const Model &model)
|
||||
{
|
||||
for (const ModelObject *model_object : model.objects)
|
||||
if (!model_object->cut_connectors.empty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool model_has_advanced_features(const Model &model)
|
||||
{
|
||||
auto config_is_advanced = [](const ModelConfig &config) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_ObjectID_hpp_
|
||||
|
||||
#include <cereal/access.hpp>
|
||||
#include <cereal/types/base_class.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -89,7 +90,9 @@ private:
|
|||
friend class cereal::access;
|
||||
friend class Slic3r::UndoRedo::StackImpl;
|
||||
template<class Archive> void serialize(Archive &ar) { ar(m_id); }
|
||||
protected: // #vbCHECKME && #ysFIXME
|
||||
ObjectBase(const ObjectID id) : m_id(id) {}
|
||||
private:
|
||||
template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
|
||||
};
|
||||
|
||||
|
@ -128,6 +131,64 @@ private:
|
|||
template<class Archive> void serialize(Archive &ar) { ar(m_timestamp); }
|
||||
};
|
||||
|
||||
class CutObjectBase : public ObjectBase
|
||||
{
|
||||
// check sum of CutParts in initial Object
|
||||
size_t m_check_sum{ 1 };
|
||||
// connectors count
|
||||
size_t m_connectors_cnt{ 0 };
|
||||
|
||||
public:
|
||||
// Default Constructor to assign an invalid ID
|
||||
CutObjectBase() : ObjectBase(-1) {}
|
||||
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
|
||||
// by an existing ID copied from elsewhere.
|
||||
CutObjectBase(int) : ObjectBase(-1) {}
|
||||
// Constructor to initialize full information from 3mf
|
||||
CutObjectBase(ObjectID id, size_t check_sum, size_t connectors_cnt) : ObjectBase(id), m_check_sum(check_sum), m_connectors_cnt(connectors_cnt) {}
|
||||
// The class tree will have virtual tables and type information.
|
||||
virtual ~CutObjectBase() = default;
|
||||
|
||||
bool operator<(const CutObjectBase& other) const { return other.id() > this->id(); }
|
||||
bool operator==(const CutObjectBase& other) const { return other.id() == this->id(); }
|
||||
|
||||
void copy(const CutObjectBase& rhs) {
|
||||
this->copy_id(rhs);
|
||||
this->m_check_sum = rhs.check_sum();
|
||||
this->m_connectors_cnt = rhs.connectors_cnt() ;
|
||||
}
|
||||
CutObjectBase& operator=(const CutObjectBase& other) {
|
||||
this->copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
set_invalid_id();
|
||||
m_check_sum = 1;
|
||||
m_connectors_cnt = 0;
|
||||
}
|
||||
|
||||
void init() { this->set_new_unique_id(); }
|
||||
bool has_same_id(const CutObjectBase& rhs) { return this->id() == rhs.id(); }
|
||||
bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() &&
|
||||
this->check_sum() == rhs.check_sum() &&
|
||||
this->connectors_cnt() == rhs.connectors_cnt() ; }
|
||||
|
||||
size_t check_sum() const { return m_check_sum; }
|
||||
void set_check_sum(size_t cs) { m_check_sum = cs; }
|
||||
void increase_check_sum(size_t cnt) { m_check_sum += cnt; }
|
||||
|
||||
size_t connectors_cnt() const { return m_connectors_cnt; }
|
||||
void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; }
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive& ar) {
|
||||
ar(cereal::base_class<ObjectBase>(this));
|
||||
ar(m_check_sum, m_connectors_cnt);
|
||||
}
|
||||
};
|
||||
|
||||
// Unique object / instance ID for the wipe tower.
|
||||
extern ObjectID wipe_tower_object_id();
|
||||
extern ObjectID wipe_tower_instance_id();
|
||||
|
|
|
@ -1061,6 +1061,61 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
|
|||
return mesh;
|
||||
}
|
||||
|
||||
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
|
||||
// Note: This function uses code for sphere generation, but for stackCount = 2;
|
||||
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
|
||||
{
|
||||
int stackCount = 2;
|
||||
float sectorStep = float(2. * M_PI / sectorCount);
|
||||
float stackStep = float(M_PI / stackCount);
|
||||
|
||||
indexed_triangle_set mesh;
|
||||
auto& vertices = mesh.vertices;
|
||||
vertices.reserve((stackCount - 1) * sectorCount + 2);
|
||||
for (int i = 0; i <= stackCount; ++i) {
|
||||
// from pi/2 to -pi/2
|
||||
double stackAngle = 0.5 * M_PI - stackStep * i;
|
||||
double xy = radius * cos(stackAngle);
|
||||
double z = radius * sin(stackAngle);
|
||||
if (i == 0 || i == stackCount)
|
||||
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
|
||||
else
|
||||
for (int j = 0; j < sectorCount; ++j) {
|
||||
// from 0 to 2pi
|
||||
double sectorAngle = sectorStep * j;
|
||||
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
|
||||
}
|
||||
}
|
||||
|
||||
auto& facets = mesh.indices;
|
||||
facets.reserve(2 * (stackCount - 1) * sectorCount);
|
||||
for (int i = 0; i < stackCount; ++i) {
|
||||
// Beginning of current stack.
|
||||
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
|
||||
int k1_first = k1;
|
||||
// Beginning of next stack.
|
||||
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
|
||||
int k2_first = k2;
|
||||
for (int j = 0; j < sectorCount; ++j) {
|
||||
// 2 triangles per sector excluding first and last stacks
|
||||
int k1_next = k1;
|
||||
int k2_next = k2;
|
||||
if (i != 0) {
|
||||
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
|
||||
facets.emplace_back(k1, k2, k1_next);
|
||||
}
|
||||
if (i + 1 != stackCount) {
|
||||
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
|
||||
facets.emplace_back(k1_next, k2, k2_next);
|
||||
}
|
||||
k1 = k1_next;
|
||||
k2 = k2_next;
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
|
||||
{
|
||||
std::vector<Vec3f> dst_vertices;
|
||||
|
|
|
@ -301,6 +301,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
|
|||
indexed_triangle_set its_make_prism(float width, float length, float height);
|
||||
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
|
||||
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
|
||||
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
|
||||
indexed_triangle_set its_make_pyramid(float base, float height);
|
||||
indexed_triangle_set its_make_sphere(double radius, double fa);
|
||||
|
||||
|
|
|
@ -262,9 +262,9 @@ constexpr inline T lerp(const T& a, const T& b, Number t)
|
|||
}
|
||||
|
||||
template <typename Number>
|
||||
constexpr inline bool is_approx(Number value, Number test_value)
|
||||
constexpr inline bool is_approx(Number value, Number test_value, Number precision = EPSILON)
|
||||
{
|
||||
return std::fabs(double(value) - double(test_value)) < double(EPSILON);
|
||||
return std::fabs(double(value) - double(test_value)) < double(precision);
|
||||
}
|
||||
|
||||
// A meta-predicate which is true for integers wider than or equal to coord_t
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue