mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-27 15:32:01 +08:00
Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_spe1784_binary_gcode
This commit is contained in:
commit
d9d771c268
1219
cmake/modules/FindwxWidgets.cmake
Normal file
1219
cmake/modules/FindwxWidgets.cmake
Normal file
File diff suppressed because it is too large
Load Diff
13
resources/icons/snap.svg
Normal file
13
resources/icons/snap.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#808080" d="M9,15H7c-0.5522847,0-1-0.4477148-1-1V5c0-0.5522847,0.4477153-1,1-1h2c0.5522852,0,1,0.4477153,1,1v9
|
||||||
|
C10,14.5522852,9.5522852,15,9,15z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-miterlimit="5" d="M1.5,11.5h2v-8c0-1.1045694,0.8954306-2,2-2h5c1.1045694,0,2,0.8954306,2,2v8h2"/>
|
||||||
|
</g>
|
||||||
|
<line fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" x1="1.5" y1="14.5" x2="14.5" y2="14.5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 846 B |
@ -49,26 +49,12 @@ if (SLIC3R_GUI)
|
|||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
|
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
|
||||||
find_package(wxWidgets 3.2 QUIET COMPONENTS base core adv html gl)
|
|
||||||
if (NOT wxWidgets_FOUND)
|
|
||||||
message(FATAL_ERROR "Could not find wxWidgets >= 3.2")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT "${wxWidgets_USE_FILE}" STREQUAL "")
|
|
||||||
include(${wxWidgets_USE_FILE})
|
|
||||||
endif ()
|
|
||||||
else ()
|
|
||||||
find_package(wxWidgets 3.2 COMPONENTS html adv gl core base)
|
|
||||||
if (NOT wxWidgets_FOUND)
|
|
||||||
message(STATUS "Trying to find wxWidgets in CONFIG mode...")
|
|
||||||
find_package(wxWidgets 3.2 CONFIG REQUIRED COMPONENTS html adv gl core base)
|
|
||||||
slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release)
|
|
||||||
else ()
|
|
||||||
if (NOT "${wxWidgets_USE_FILE}" STREQUAL "")
|
|
||||||
include(${wxWidgets_USE_FILE})
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endif ()
|
endif ()
|
||||||
|
find_package(wxWidgets 3.2 MODULE REQUIRED COMPONENTS base core adv html gl)
|
||||||
|
|
||||||
|
include(${wxWidgets_USE_FILE})
|
||||||
|
|
||||||
|
slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/CutUtils.hpp"
|
||||||
#include "libslic3r/ModelArrange.hpp"
|
#include "libslic3r/ModelArrange.hpp"
|
||||||
#include "libslic3r/Platform.hpp"
|
#include "libslic3r/Platform.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
@ -437,8 +438,11 @@ int CLI::run(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
|
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
|
||||||
model.objects.front()->cut(0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
|
Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
|
||||||
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
||||||
|
auto cut_objects = cut.perform_with_plane();
|
||||||
|
for (ModelObject* obj : cut_objects)
|
||||||
|
model.add_object(*obj);
|
||||||
#endif
|
#endif
|
||||||
model.delete_object(size_t(0));
|
model.delete_object(size_t(0));
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,7 @@ namespace ImGui
|
|||||||
// const wchar_t MmuSegmentationMarker = 0x1F;
|
// const wchar_t MmuSegmentationMarker = 0x1F;
|
||||||
const wchar_t PlugMarker = 0x1C;
|
const wchar_t PlugMarker = 0x1C;
|
||||||
const wchar_t DowelMarker = 0x1D;
|
const wchar_t DowelMarker = 0x1D;
|
||||||
|
const wchar_t SnapMarker = 0x1E;
|
||||||
// Do not forget use following letters only in wstring
|
// Do not forget use following letters only in wstring
|
||||||
const wchar_t DocumentationButton = 0x2600;
|
const wchar_t DocumentationButton = 0x2600;
|
||||||
const wchar_t DocumentationHoverButton = 0x2601;
|
const wchar_t DocumentationHoverButton = 0x2601;
|
||||||
|
@ -206,6 +206,8 @@ set(SLIC3R_SOURCES
|
|||||||
BlacklistedLibraryCheck.hpp
|
BlacklistedLibraryCheck.hpp
|
||||||
LocalesUtils.cpp
|
LocalesUtils.cpp
|
||||||
LocalesUtils.hpp
|
LocalesUtils.hpp
|
||||||
|
CutUtils.cpp
|
||||||
|
CutUtils.hpp
|
||||||
Model.cpp
|
Model.cpp
|
||||||
Model.hpp
|
Model.hpp
|
||||||
ModelArrange.hpp
|
ModelArrange.hpp
|
||||||
|
645
src/libslic3r/CutUtils.cpp
Normal file
645
src/libslic3r/CutUtils.cpp
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
|
||||||
|
#include "CutUtils.hpp"
|
||||||
|
#include "Geometry.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "Model.hpp"
|
||||||
|
#include "TriangleMeshSlicer.hpp"
|
||||||
|
#include "TriangleSelector.hpp"
|
||||||
|
#include "ObjectID.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
using namespace Geometry;
|
||||||
|
|
||||||
|
static void apply_tolerance(ModelVolume* vol)
|
||||||
|
{
|
||||||
|
ModelVolume::CutInfo& cut_info = vol->cut_info;
|
||||||
|
|
||||||
|
assert(cut_info.is_connector);
|
||||||
|
if (!cut_info.is_processed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vec3d sf = vol->get_scaling_factor();
|
||||||
|
|
||||||
|
// make a "hole" wider
|
||||||
|
sf[X] += double(cut_info.radius_tolerance);
|
||||||
|
sf[Y] += double(cut_info.radius_tolerance);
|
||||||
|
|
||||||
|
// make a "hole" dipper
|
||||||
|
sf[Z] += double(cut_info.height_tolerance);
|
||||||
|
|
||||||
|
vol->set_scaling_factor(sf);
|
||||||
|
|
||||||
|
// correct offset in respect to the new depth
|
||||||
|
Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ();
|
||||||
|
if (rot_norm.norm() != 0.0)
|
||||||
|
rot_norm.normalize();
|
||||||
|
|
||||||
|
double z_offset = 0.5 * static_cast<double>(cut_info.height_tolerance);
|
||||||
|
if (cut_info.connector_type == CutConnectorType::Plug ||
|
||||||
|
cut_info.connector_type == CutConnectorType::Snap)
|
||||||
|
z_offset -= 0.05; // add small Z offset to better preview
|
||||||
|
|
||||||
|
vol->set_offset(vol->get_offset() + rot_norm * z_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
|
||||||
|
{
|
||||||
|
if (mesh.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mesh.transform(cut_matrix);
|
||||||
|
ModelVolume* vol = object->add_volume(mesh);
|
||||||
|
vol->set_type(type);
|
||||||
|
|
||||||
|
vol->name = src_volume->name + suffix;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_volume_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh)
|
||||||
|
{
|
||||||
|
const auto volume_matrix = volume->get_matrix();
|
||||||
|
|
||||||
|
const Transformation cut_transformation = Transformation(cut_matrix);
|
||||||
|
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.
|
||||||
|
TriangleMesh mesh(volume->mesh());
|
||||||
|
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
||||||
|
std::vector<ModelObject*>& dowels)
|
||||||
|
{
|
||||||
|
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 (volume->cut_info.connector_type != CutConnectorType::Dowel) {
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
|
||||||
|
ModelVolume* vol = nullptr;
|
||||||
|
if (volume->cut_info.connector_type == CutConnectorType::Snap) {
|
||||||
|
TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.));
|
||||||
|
|
||||||
|
vol = upper->add_volume(std::move(mesh));
|
||||||
|
vol->set_transformation(volume->get_transformation());
|
||||||
|
vol->set_type(ModelVolumeType::NEGATIVE_VOLUME);
|
||||||
|
|
||||||
|
vol->cut_info = volume->cut_info;
|
||||||
|
vol->name = volume->name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vol = upper->add_volume(*volume);
|
||||||
|
|
||||||
|
vol->set_transformation(volume_matrix);
|
||||||
|
apply_tolerance(vol);
|
||||||
|
}
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||||
|
ModelVolume* vol = lower->add_volume(*volume);
|
||||||
|
vol->set_transformation(volume_matrix);
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
|
||||||
|
ModelObject* dowel{ nullptr };
|
||||||
|
// Clone the object to duplicate instances, materials etc.
|
||||||
|
volume->get_object()->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);
|
||||||
|
|
||||||
|
dowels.push_back(dowel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut the dowel
|
||||||
|
apply_tolerance(volume);
|
||||||
|
|
||||||
|
// Perform cut
|
||||||
|
TriangleMesh upper_mesh, lower_mesh;
|
||||||
|
process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh);
|
||||||
|
|
||||||
|
// add small Z offset to better preview
|
||||||
|
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
|
||||||
|
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
|
||||||
|
|
||||||
|
// Add cut parts to the related objects
|
||||||
|
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
|
||||||
|
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void 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(Transformation(volume_matrix));
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
||||||
|
upper->add_volume(*volume);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
|
||||||
|
{
|
||||||
|
// Perform cut
|
||||||
|
TriangleMesh upper_mesh, lower_mesh;
|
||||||
|
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
|
||||||
|
|
||||||
|
// Add required cut parts to the objects
|
||||||
|
|
||||||
|
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
||||||
|
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
|
||||||
|
if (!lower_mesh.empty()) {
|
||||||
|
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
|
||||||
|
upper->volumes.back()->cut_info.is_from_upper = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx,
|
||||||
|
const Transform3d& cut_matrix = Transform3d::Identity(),
|
||||||
|
bool place_on_cut = false, bool flip = false)
|
||||||
|
{
|
||||||
|
// 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 double rot_z = obj_instance->get_rotation().z();
|
||||||
|
|
||||||
|
Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor());
|
||||||
|
// add respect to mirroring
|
||||||
|
if (obj_instance->is_left_handed())
|
||||||
|
inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1)));
|
||||||
|
|
||||||
|
obj_instance->set_transformation(inst_trafo);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes/*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts*/)
|
||||||
|
: m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes)
|
||||||
|
{
|
||||||
|
m_model = Model();
|
||||||
|
if (object)
|
||||||
|
m_model.add_object(*object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cut::post_process(ModelObject* object, ModelObjectPtrs& cut_object_ptrs, bool keep, bool place_on_cut, bool flip)
|
||||||
|
{
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
if (keep && !object->volumes.empty()) {
|
||||||
|
reset_instance_transformation(object, m_instance, m_cut_matrix, place_on_cut, flip);
|
||||||
|
cut_object_ptrs.push_back(object);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_model.objects.push_back(object); // will be deleted in m_model.clear_objects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cut::post_process(ModelObject* upper, ModelObject* lower, ModelObjectPtrs& cut_object_ptrs)
|
||||||
|
{
|
||||||
|
post_process(upper, cut_object_ptrs,
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::KeepUpper),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::FlipUpper));
|
||||||
|
|
||||||
|
post_process(lower, cut_object_ptrs,
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::KeepLower),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
||||||
|
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Cut::finalize(const ModelObjectPtrs& objects)
|
||||||
|
{
|
||||||
|
//clear model from temporarry objects
|
||||||
|
m_model.clear_objects();
|
||||||
|
|
||||||
|
// add to model result objects
|
||||||
|
m_model.objects = objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ModelObjectPtrs& Cut::perform_with_plane()
|
||||||
|
{
|
||||||
|
if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) {
|
||||||
|
m_model.clear_objects();
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObject* mo = m_model.objects.front();
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
|
||||||
|
|
||||||
|
// Clone the object to duplicate instances, materials etc.
|
||||||
|
ModelObject* upper{ nullptr };
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||||
|
mo->clone_for_cut(&upper);
|
||||||
|
|
||||||
|
ModelObject* lower{ nullptr };
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::KeepAsParts))
|
||||||
|
mo->clone_for_cut(&lower);
|
||||||
|
|
||||||
|
std::vector<ModelObject*> dowels;
|
||||||
|
|
||||||
|
// Because transformations are going to be applied to meshes directly,
|
||||||
|
// we reset transformation of all instances and volumes,
|
||||||
|
// except for translation and Z-rotation on instances, which are preserved
|
||||||
|
// in the transformation matrix and not applied to the mesh transform.
|
||||||
|
|
||||||
|
const auto instance_matrix = mo->instances[m_instance]->get_transformation().get_matrix_no_offset();
|
||||||
|
const Transformation cut_transformation = Transformation(m_cut_matrix);
|
||||||
|
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
|
||||||
|
|
||||||
|
for (ModelVolume* volume : mo->volumes) {
|
||||||
|
volume->reset_extra_facets();
|
||||||
|
|
||||||
|
if (!volume->is_model_part()) {
|
||||||
|
if (volume->cut_info.is_processed)
|
||||||
|
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower);
|
||||||
|
else
|
||||||
|
process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels);
|
||||||
|
}
|
||||||
|
else if (!volume->mesh().empty())
|
||||||
|
process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post-process cut parts
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) {
|
||||||
|
m_model = Model();
|
||||||
|
m_model.objects.push_back(upper);
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObjectPtrs cut_object_ptrs;
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) {
|
||||||
|
reset_instance_transformation(upper, m_instance, m_cut_matrix);
|
||||||
|
cut_object_ptrs.push_back(upper);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Delete all modifiers which are not intersecting with solid parts bounding box
|
||||||
|
auto delete_extra_modifiers = [this](ModelObject* mo) {
|
||||||
|
if (!mo) return;
|
||||||
|
const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance);
|
||||||
|
const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix();
|
||||||
|
|
||||||
|
for (int i = int(mo->volumes.size()) - 1; i >= 0; --i)
|
||||||
|
if (const ModelVolume* vol = mo->volumes[i];
|
||||||
|
!vol->is_model_part() && !vol->is_cut_connector()) {
|
||||||
|
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
|
||||||
|
if (!obj_bb.intersects(bb))
|
||||||
|
mo->delete_volume(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
post_process(upper, lower, cut_object_ptrs);
|
||||||
|
delete_extra_modifiers(upper);
|
||||||
|
delete_extra_modifiers(lower);
|
||||||
|
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
||||||
|
for (auto dowel : dowels) {
|
||||||
|
reset_instance_transformation(dowel, m_instance);
|
||||||
|
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
||||||
|
cut_object_ptrs.push_back(dowel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
||||||
|
|
||||||
|
finalize(cut_object_ptrs);
|
||||||
|
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void distribute_modifiers_from_object(ModelObject* from_obj, const int instance_idx, ModelObject* to_obj1, ModelObject* to_obj2)
|
||||||
|
{
|
||||||
|
auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3();
|
||||||
|
auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3();
|
||||||
|
const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix();
|
||||||
|
|
||||||
|
for (ModelVolume* vol : from_obj->volumes)
|
||||||
|
if (!vol->is_model_part()) {
|
||||||
|
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
|
||||||
|
// Don't add modifiers which are not intersecting with solid parts
|
||||||
|
if (obj1_bb.intersects(bb))
|
||||||
|
to_obj1->add_volume(*vol);
|
||||||
|
if (obj2_bb.intersects(bb))
|
||||||
|
to_obj2->add_volume(*vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void merge_solid_parts_inside_object(ModelObjectPtrs& objects)
|
||||||
|
{
|
||||||
|
for (ModelObject* mo : objects) {
|
||||||
|
TriangleMesh mesh;
|
||||||
|
// Merge all SolidPart but not Connectors
|
||||||
|
for (const ModelVolume* mv : mo->volumes) {
|
||||||
|
if (mv->is_model_part() && !mv->is_cut_connector()) {
|
||||||
|
TriangleMesh m = mv->mesh();
|
||||||
|
m.transform(mv->get_matrix());
|
||||||
|
mesh.merge(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!mesh.empty()) {
|
||||||
|
ModelVolume* new_volume = mo->add_volume(mesh);
|
||||||
|
new_volume->name = mo->name;
|
||||||
|
// Delete all merged SolidPart but not Connectors
|
||||||
|
for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) {
|
||||||
|
const ModelVolume* mv = mo->volumes[i];
|
||||||
|
if (mv->is_model_part() && !mv->is_cut_connector())
|
||||||
|
mo->delete_volume(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ModelObjectPtrs& Cut::perform_by_contour(std::vector<Part> parts, int dowels_count)
|
||||||
|
{
|
||||||
|
ModelObject* cut_mo = m_model.objects.front();
|
||||||
|
|
||||||
|
// Clone the object to duplicate instances, materials etc.
|
||||||
|
ModelObject* upper{ nullptr };
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_mo->clone_for_cut(&upper);
|
||||||
|
ModelObject* lower{ nullptr };
|
||||||
|
if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower);
|
||||||
|
|
||||||
|
const size_t cut_parts_cnt = parts.size();
|
||||||
|
bool has_modifiers = false;
|
||||||
|
|
||||||
|
// Distribute SolidParts to the Upper/Lower object
|
||||||
|
for (size_t id = 0; id < cut_parts_cnt; ++id) {
|
||||||
|
if (parts[id].is_modifier)
|
||||||
|
has_modifiers = true; // modifiers will be added later to the related parts
|
||||||
|
else if (ModelObject* obj = (parts[id].selected ? upper : lower))
|
||||||
|
obj->add_volume(*(cut_mo->volumes[id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_modifiers) {
|
||||||
|
// Distribute Modifiers to the Upper/Lower object
|
||||||
|
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObjectPtrs cut_object_ptrs;
|
||||||
|
|
||||||
|
ModelVolumePtrs& volumes = cut_mo->volumes;
|
||||||
|
if (volumes.size() == cut_parts_cnt) {
|
||||||
|
// Means that object is cut without connectors
|
||||||
|
|
||||||
|
// Just add Upper and Lower objects to cut_object_ptrs
|
||||||
|
post_process(upper, lower, cut_object_ptrs);
|
||||||
|
}
|
||||||
|
else if (volumes.size() > cut_parts_cnt) {
|
||||||
|
// Means that object is cut with connectors
|
||||||
|
|
||||||
|
// All volumes are distributed to Upper / Lower object,
|
||||||
|
// So we don’t need them anymore
|
||||||
|
for (size_t id = 0; id < cut_parts_cnt; id++)
|
||||||
|
delete* (volumes.begin() + id);
|
||||||
|
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
|
||||||
|
|
||||||
|
// Perform cut just to get connectors
|
||||||
|
Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes);
|
||||||
|
const ModelObjectPtrs& cut_connectors_obj = cut.perform_with_plane();
|
||||||
|
assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
|
||||||
|
|
||||||
|
// Connectors from upper object
|
||||||
|
for (const ModelVolume* volume : cut_connectors_obj[0]->volumes)
|
||||||
|
upper->add_volume(*volume, volume->type());
|
||||||
|
|
||||||
|
// Connectors from lower object
|
||||||
|
for (const ModelVolume* volume : cut_connectors_obj[1]->volumes)
|
||||||
|
lower->add_volume(*volume, volume->type());
|
||||||
|
|
||||||
|
// Add Upper and Lower objects to cut_object_ptrs
|
||||||
|
post_process(upper, lower, cut_object_ptrs);
|
||||||
|
|
||||||
|
// Add Dowel-connectors as separate objects to cut_object_ptrs
|
||||||
|
if (cut_connectors_obj.size() >= 3)
|
||||||
|
for (size_t id = 2; id < cut_connectors_obj.size(); id++)
|
||||||
|
cut_object_ptrs.push_back(cut_connectors_obj[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now merge all model parts together:
|
||||||
|
merge_solid_parts_inside_object(cut_object_ptrs);
|
||||||
|
|
||||||
|
finalize(cut_object_ptrs);
|
||||||
|
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/)
|
||||||
|
{
|
||||||
|
ModelObject* cut_mo = m_model.objects.front();
|
||||||
|
|
||||||
|
// Clone the object to duplicate instances, materials etc.
|
||||||
|
ModelObject* upper{ nullptr };
|
||||||
|
cut_mo->clone_for_cut(&upper);
|
||||||
|
ModelObject* lower{ nullptr };
|
||||||
|
cut_mo->clone_for_cut(&lower);
|
||||||
|
|
||||||
|
const double groove_half_depth = 0.5 * double(groove.depth);
|
||||||
|
|
||||||
|
Model tmp_model_for_cut = Model();
|
||||||
|
|
||||||
|
Model tmp_model = Model();
|
||||||
|
tmp_model.add_object(*cut_mo);
|
||||||
|
ModelObject* tmp_object = tmp_model.objects.front();
|
||||||
|
|
||||||
|
auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) {
|
||||||
|
const auto& volumes = tmp_model_for_cut.objects.front()->volumes;
|
||||||
|
for (const ModelVolume* volume : volumes)
|
||||||
|
if (volume->is_model_part()) {
|
||||||
|
if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) ||
|
||||||
|
(attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) {
|
||||||
|
ModelVolume* new_vol = object->add_volume(*volume);
|
||||||
|
new_vol->reset_from_upper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto cut = [this, add_volumes_from_cut]
|
||||||
|
(ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) {
|
||||||
|
Cut cut(object, m_instance, cut_matrix);
|
||||||
|
|
||||||
|
tmp_model_for_cut = Model();
|
||||||
|
tmp_model_for_cut.add_object(*cut.perform_with_plane().front());
|
||||||
|
assert(!tmp_model_for_cut.objects.empty());
|
||||||
|
|
||||||
|
object->clear_volumes();
|
||||||
|
add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut);
|
||||||
|
reset_instance_transformation(object, m_instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
// cut by upper plane
|
||||||
|
|
||||||
|
const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
|
||||||
|
{
|
||||||
|
cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut by lower plane
|
||||||
|
|
||||||
|
const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
|
||||||
|
{
|
||||||
|
cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut middle part with 2 angles and add parts to related upper/lower objects
|
||||||
|
|
||||||
|
const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle));
|
||||||
|
|
||||||
|
// cut by angle1 plane
|
||||||
|
{
|
||||||
|
const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
|
||||||
|
|
||||||
|
cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut by angle2 plane
|
||||||
|
{
|
||||||
|
const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
|
||||||
|
|
||||||
|
cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply tolerance to the middle part
|
||||||
|
{
|
||||||
|
const double h_groove_shift_tolerance = groove_half_depth - (double)groove.depth_tolerance;
|
||||||
|
|
||||||
|
const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix;
|
||||||
|
cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
|
||||||
|
const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance);
|
||||||
|
|
||||||
|
const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
|
||||||
|
cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
|
||||||
|
const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
|
||||||
|
cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this part can be added to the upper object now
|
||||||
|
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
|
||||||
|
|
||||||
|
ModelObjectPtrs cut_object_ptrs;
|
||||||
|
|
||||||
|
if (keep_as_parts) {
|
||||||
|
// add volumes from lower object to the upper, but mark them as a lower
|
||||||
|
const auto& volumes = lower->volumes;
|
||||||
|
for (const ModelVolume* volume : volumes) {
|
||||||
|
ModelVolume* new_vol = upper->add_volume(*volume);
|
||||||
|
new_vol->cut_info.is_from_upper = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add modifiers
|
||||||
|
for (const ModelVolume* volume : cut_mo->volumes)
|
||||||
|
if (!volume->is_model_part())
|
||||||
|
upper->add_volume(*volume);
|
||||||
|
|
||||||
|
cut_object_ptrs.push_back(upper);
|
||||||
|
|
||||||
|
// add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks
|
||||||
|
cut_object_ptrs.push_back(lower);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// add modifiers if object has any
|
||||||
|
for (const ModelVolume* volume : cut_mo->volumes)
|
||||||
|
if (!volume->is_model_part()) {
|
||||||
|
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!upper->volumes.empty() && !lower->volumes.empty());
|
||||||
|
|
||||||
|
// Add Upper and Lower parts to cut_object_ptrs
|
||||||
|
|
||||||
|
post_process(upper, lower, cut_object_ptrs);
|
||||||
|
|
||||||
|
// Now merge all model parts together:
|
||||||
|
merge_solid_parts_inside_object(cut_object_ptrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(cut_object_ptrs);
|
||||||
|
|
||||||
|
return m_model.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
66
src/libslic3r/CutUtils.hpp
Normal file
66
src/libslic3r/CutUtils.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef slic3r_CutUtils_hpp_
|
||||||
|
#define slic3r_CutUtils_hpp_
|
||||||
|
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
|
#include "Point.hpp"
|
||||||
|
#include "Model.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
using ModelObjectPtrs = std::vector<ModelObject*>;
|
||||||
|
|
||||||
|
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
|
||||||
|
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
||||||
|
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
||||||
|
|
||||||
|
|
||||||
|
class Cut {
|
||||||
|
|
||||||
|
Model m_model;
|
||||||
|
int m_instance;
|
||||||
|
const Transform3d m_cut_matrix;
|
||||||
|
ModelObjectCutAttributes m_attributes;
|
||||||
|
|
||||||
|
void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip);
|
||||||
|
void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects);
|
||||||
|
void finalize(const ModelObjectPtrs& objects);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
|
||||||
|
ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper |
|
||||||
|
ModelObjectCutAttribute::KeepLower |
|
||||||
|
ModelObjectCutAttribute::KeepAsParts );
|
||||||
|
~Cut() { m_model.clear_objects(); }
|
||||||
|
|
||||||
|
struct Groove
|
||||||
|
{
|
||||||
|
float depth{ 0.f };
|
||||||
|
float width{ 0.f };
|
||||||
|
float flaps_angle{ 0.f };
|
||||||
|
float angle{ 0.f };
|
||||||
|
float depth_init{ 0.f };
|
||||||
|
float width_init{ 0.f };
|
||||||
|
float flaps_angle_init{ 0.f };
|
||||||
|
float angle_init{ 0.f };
|
||||||
|
float depth_tolerance{ 0.1f };
|
||||||
|
float width_tolerance{ 0.1f };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Part
|
||||||
|
{
|
||||||
|
bool selected;
|
||||||
|
bool is_modifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModelObjectPtrs& perform_with_plane();
|
||||||
|
const ModelObjectPtrs& perform_by_contour(std::vector<Part> parts, int dowels_count);
|
||||||
|
const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false);
|
||||||
|
|
||||||
|
}; // namespace Cut
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* slic3r_CutUtils_hpp_ */
|
@ -146,67 +146,68 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
|
|||||||
std::vector<float> angles_for_curvature(points.size());
|
std::vector<float> angles_for_curvature(points.size());
|
||||||
std::vector<float> distances_for_curvature(points.size());
|
std::vector<float> distances_for_curvature(points.size());
|
||||||
|
|
||||||
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
|
for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) {
|
||||||
ExtendedPoint &a = points[point_idx];
|
ExtendedPoint &a = points[point_idx];
|
||||||
ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
|
size_t prev = prev_idx_modulo(point_idx, points.size());
|
||||||
|
size_t next = next_idx_modulo(point_idx, points.size());
|
||||||
|
|
||||||
int prev_point_idx = point_idx;
|
int iter_limit = points.size();
|
||||||
while (prev_point_idx > 0) {
|
while ((a.position - points[prev].position).squaredNorm() < 1 && iter_limit > 0) {
|
||||||
prev_point_idx--;
|
prev = prev_idx_modulo(prev, points.size());
|
||||||
if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) {
|
iter_limit--;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int next_point_index = point_idx;
|
while ((a.position - points[next].position).squaredNorm() < 1 && iter_limit > 0) {
|
||||||
while (next_point_index < int(points.size()) - 1) {
|
next = next_idx_modulo(next, points.size());
|
||||||
next_point_index++;
|
iter_limit--;
|
||||||
if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
distances_for_curvature[point_idx] = (prev.position - a.position).norm();
|
distances_for_curvature[point_idx] = (points[prev].position - a.position).norm();
|
||||||
if (prev_point_idx != point_idx && next_point_index != point_idx) {
|
float alfa = angle(a.position - points[prev].position, points[next].position - a.position);
|
||||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
angles_for_curvature[point_idx] = alfa;
|
||||||
angles_for_curvature[point_idx] = alfa;
|
|
||||||
} // else keep zero
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (float window_size : {3.0f, 9.0f, 16.0f}) {
|
if (std::accumulate(distances_for_curvature.begin(), distances_for_curvature.end(), 0) > EPSILON)
|
||||||
size_t tail_point = 0;
|
for (float window_size : {3.0f, 9.0f, 16.0f}) {
|
||||||
float tail_window_acc = 0;
|
size_t tail_point = 0;
|
||||||
float tail_angle_acc = 0;
|
float tail_window_acc = 0;
|
||||||
|
float tail_angle_acc = 0;
|
||||||
|
|
||||||
size_t head_point = 0;
|
size_t head_point = 0;
|
||||||
float head_window_acc = 0;
|
float head_window_acc = 0;
|
||||||
float head_angle_acc = 0;
|
float head_angle_acc = 0;
|
||||||
|
|
||||||
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
|
for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) {
|
||||||
if (point_idx > 0) {
|
if (point_idx == 0) {
|
||||||
tail_window_acc += distances_for_curvature[point_idx - 1];
|
while (tail_window_acc < window_size * 0.5) {
|
||||||
tail_angle_acc += angles_for_curvature[point_idx - 1];
|
tail_window_acc += distances_for_curvature[tail_point];
|
||||||
head_window_acc -= distances_for_curvature[point_idx - 1];
|
tail_angle_acc += angles_for_curvature[tail_point];
|
||||||
head_angle_acc -= angles_for_curvature[point_idx - 1];
|
tail_point = prev_idx_modulo(tail_point, points.size());
|
||||||
}
|
}
|
||||||
while (tail_window_acc > window_size * 0.5 && int(tail_point) < point_idx) {
|
}
|
||||||
tail_window_acc -= distances_for_curvature[tail_point];
|
while (tail_window_acc - distances_for_curvature[next_idx_modulo(tail_point, points.size())] > window_size * 0.5) {
|
||||||
tail_angle_acc -= angles_for_curvature[tail_point];
|
tail_point = next_idx_modulo(tail_point, points.size());
|
||||||
tail_point++;
|
tail_window_acc -= distances_for_curvature[tail_point];
|
||||||
}
|
tail_angle_acc -= angles_for_curvature[tail_point];
|
||||||
|
}
|
||||||
|
|
||||||
while (head_window_acc < window_size * 0.5 && int(head_point) < int(points.size()) - 1) {
|
while (head_window_acc < window_size * 0.5) {
|
||||||
head_window_acc += distances_for_curvature[head_point];
|
head_point = next_idx_modulo(head_point, points.size());
|
||||||
head_angle_acc += angles_for_curvature[head_point];
|
head_window_acc += distances_for_curvature[head_point];
|
||||||
head_point++;
|
head_angle_acc += angles_for_curvature[head_point];
|
||||||
}
|
}
|
||||||
|
|
||||||
float curvature = (tail_angle_acc + head_angle_acc) / (tail_window_acc + head_window_acc);
|
float curvature = (tail_angle_acc + head_angle_acc) / window_size;
|
||||||
if (std::abs(curvature) > std::abs(points[point_idx].curvature)) {
|
if (std::abs(curvature) > std::abs(points[point_idx].curvature)) {
|
||||||
points[point_idx].curvature = curvature;
|
points[point_idx].curvature = curvature;
|
||||||
|
}
|
||||||
|
|
||||||
|
tail_window_acc += distances_for_curvature[point_idx];
|
||||||
|
tail_angle_acc += angles_for_curvature[point_idx];
|
||||||
|
head_window_acc -= distances_for_curvature[point_idx];
|
||||||
|
head_angle_acc -= angles_for_curvature[point_idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
@ -1280,64 +1280,6 @@ bool ModelObject::has_connectors() const
|
|||||||
return false;
|
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::Prism)
|
|
||||||
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
|
|
||||||
else if (connector_attributes.type == CutConnectorType::Plug)
|
|
||||||
connector_mesh = its_make_frustum(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(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 };
|
|
||||||
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()
|
void ModelObject::invalidate_cut()
|
||||||
{
|
{
|
||||||
this->cut_id.invalidate();
|
this->cut_id.invalidate();
|
||||||
@ -1390,297 +1332,6 @@ void ModelVolume::reset_extra_facets()
|
|||||||
this->mmu_segmentation_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();
|
|
||||||
|
|
||||||
// make a "hole" wider
|
|
||||||
sf[X] += double(cut_info.radius_tolerance);
|
|
||||||
sf[Y] += double(cut_info.radius_tolerance);
|
|
||||||
|
|
||||||
// make a "hole" dipper
|
|
||||||
sf[Z] += double(cut_info.height_tolerance);
|
|
||||||
|
|
||||||
set_scaling_factor(sf);
|
|
||||||
|
|
||||||
// correct offset in respect to the new depth
|
|
||||||
Vec3d rot_norm = Geometry::rotation_transform(get_rotation()) * Vec3d::UnitZ();
|
|
||||||
if (rot_norm.norm() != 0.0)
|
|
||||||
rot_norm.normalize();
|
|
||||||
|
|
||||||
double z_offset = 0.5 * static_cast<double>(cut_info.height_tolerance);
|
|
||||||
if (cut_info.connector_type == CutConnectorType::Plug)
|
|
||||||
z_offset -= 0.05; // add small Z offset to better preview
|
|
||||||
|
|
||||||
set_offset(get_offset() + rot_norm * z_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
|
|
||||||
{
|
|
||||||
if (mesh.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
mesh.transform(cut_matrix);
|
|
||||||
ModelVolume* vol = object->add_volume(mesh);
|
|
||||||
vol->set_type(type);
|
|
||||||
|
|
||||||
vol->name = src_volume->name + suffix;
|
|
||||||
// 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_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
|
||||||
std::vector<ModelObject*>& dowels)
|
|
||||||
{
|
|
||||||
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 (volume->cut_info.connector_type != CutConnectorType::Dowel) {
|
|
||||||
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);
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (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);
|
|
||||||
|
|
||||||
dowels.push_back(dowel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cut the dowel
|
|
||||||
volume->apply_tolerance();
|
|
||||||
|
|
||||||
// Perform cut
|
|
||||||
TriangleMesh upper_mesh, lower_mesh;
|
|
||||||
process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh);
|
|
||||||
|
|
||||||
// add small Z offset to better preview
|
|
||||||
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
|
|
||||||
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
|
|
||||||
|
|
||||||
// Add cut parts to the related objects
|
|
||||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
|
|
||||||
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
|
||||||
upper->add_volume(*volume);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh)
|
|
||||||
{
|
|
||||||
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() * 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.
|
|
||||||
TriangleMesh mesh(volume->mesh());
|
|
||||||
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
|
|
||||||
{
|
|
||||||
// Perform cut
|
|
||||||
TriangleMesh upper_mesh, lower_mesh;
|
|
||||||
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
|
|
||||||
|
|
||||||
// Add required cut parts to the objects
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
|
|
||||||
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
|
|
||||||
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
|
|
||||||
bool place_on_cut/* = false*/, bool flip/* = false*/)
|
|
||||||
{
|
|
||||||
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 double rot_z = obj_instance->get_rotation().z();
|
|
||||||
|
|
||||||
Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor());
|
|
||||||
if (obj_instance->is_left_handed())
|
|
||||||
inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1)));
|
|
||||||
|
|
||||||
obj_instance->set_transformation(inst_trafo);
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
// Clone the object to duplicate instances, materials etc.
|
|
||||||
ModelObject* upper{ nullptr };
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
|
||||||
clone_for_cut(&upper);
|
|
||||||
|
|
||||||
ModelObject* lower{ nullptr };
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::KeepAsParts))
|
|
||||||
clone_for_cut(&lower);
|
|
||||||
|
|
||||||
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,
|
|
||||||
// except for translation and Z-rotation on instances, which are preserved
|
|
||||||
// in the transformation matrix and not applied to the mesh transform.
|
|
||||||
|
|
||||||
// const auto instance_matrix = instances[instance]->get_matrix(true);
|
|
||||||
const auto instance_matrix = instances[instance]->get_transformation().get_matrix_no_offset();
|
|
||||||
const Transformation cut_transformation = Transformation(cut_matrix);
|
|
||||||
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
|
|
||||||
|
|
||||||
for (ModelVolume* volume : volumes) {
|
|
||||||
volume->reset_extra_facets();
|
|
||||||
|
|
||||||
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, instance_matrix, cut_matrix, attributes, upper, lower, dowels);
|
|
||||||
}
|
|
||||||
else if (!volume->mesh().empty())
|
|
||||||
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-process cut parts
|
|
||||||
|
|
||||||
ModelObjectPtrs res;
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) {
|
|
||||||
reset_instance_transformation(upper, instance, cut_matrix);
|
|
||||||
res.push_back(upper);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) {
|
|
||||||
reset_instance_transformation(upper, instance, cut_matrix,
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
|
|
||||||
attributes.has(ModelObjectCutAttribute::FlipUpper));
|
|
||||||
res.push_back(upper);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) {
|
|
||||||
reset_instance_transformation(lower, instance, cut_matrix,
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
|
|
||||||
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower));
|
|
||||||
res.push_back(lower);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
|
|
||||||
for (auto dowel : dowels) {
|
|
||||||
reset_instance_transformation(dowel, instance, Transform3d::Identity());
|
|
||||||
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
|
|
||||||
res.push_back(dowel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
|
/// Compare TriangleMeshes by Bounding boxes (mainly for sort)
|
||||||
|
@ -224,6 +224,7 @@ private:
|
|||||||
enum class CutConnectorType : int {
|
enum class CutConnectorType : int {
|
||||||
Plug
|
Plug
|
||||||
, Dowel
|
, Dowel
|
||||||
|
, Snap
|
||||||
, Undef
|
, Undef
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -316,10 +317,6 @@ enum class ModelVolumeType : int {
|
|||||||
SUPPORT_ENFORCER,
|
SUPPORT_ENFORCER,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
|
|
||||||
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
|
|
||||||
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
|
|
||||||
|
|
||||||
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
|
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
|
||||||
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
|
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
|
||||||
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
|
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
|
||||||
@ -461,29 +458,12 @@ public:
|
|||||||
size_t materials_count() const;
|
size_t materials_count() const;
|
||||||
size_t facets_count() const;
|
size_t facets_count() const;
|
||||||
size_t parts_count() const;
|
size_t parts_count() const;
|
||||||
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
|
|
||||||
void apply_cut_connectors(const std::string& name);
|
|
||||||
// invalidate cut state for this object and its connectors/volumes
|
// invalidate cut state for this object and its connectors/volumes
|
||||||
void invalidate_cut();
|
void invalidate_cut();
|
||||||
// delete volumes which are marked as connector for this object
|
// delete volumes which are marked as connector for this object
|
||||||
void delete_connectors();
|
void delete_connectors();
|
||||||
void clone_for_cut(ModelObject **obj);
|
void clone_for_cut(ModelObject **obj);
|
||||||
|
|
||||||
private:
|
|
||||||
void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
|
||||||
std::vector<ModelObject*>& dowels);
|
|
||||||
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
|
|
||||||
void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh);
|
|
||||||
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
|
||||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
|
|
||||||
public:
|
|
||||||
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix,
|
|
||||||
bool place_on_cut = false, bool flip = false);
|
|
||||||
|
|
||||||
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);
|
|
||||||
void split(ModelObjectPtrs*new_objects);
|
void split(ModelObjectPtrs*new_objects);
|
||||||
void merge();
|
void merge();
|
||||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||||
@ -777,6 +757,7 @@ public:
|
|||||||
// It contains information about connetors
|
// It contains information about connetors
|
||||||
struct CutInfo
|
struct CutInfo
|
||||||
{
|
{
|
||||||
|
bool is_from_upper{ true };
|
||||||
bool is_connector{ false };
|
bool is_connector{ false };
|
||||||
bool is_processed{ true };
|
bool is_processed{ true };
|
||||||
CutConnectorType connector_type{ CutConnectorType::Plug };
|
CutConnectorType connector_type{ CutConnectorType::Plug };
|
||||||
@ -794,6 +775,7 @@ public:
|
|||||||
|
|
||||||
void set_processed() { is_processed = true; }
|
void set_processed() { is_processed = true; }
|
||||||
void invalidate() { is_connector = false; }
|
void invalidate() { is_connector = false; }
|
||||||
|
void reset_from_upper() { is_from_upper = true; }
|
||||||
|
|
||||||
template<class Archive> inline void serialize(Archive& ar) {
|
template<class Archive> inline void serialize(Archive& ar) {
|
||||||
ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance);
|
ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance);
|
||||||
@ -801,6 +783,9 @@ public:
|
|||||||
};
|
};
|
||||||
CutInfo cut_info;
|
CutInfo cut_info;
|
||||||
|
|
||||||
|
bool is_from_upper() const { return cut_info.is_from_upper; }
|
||||||
|
void reset_from_upper() { cut_info.reset_from_upper(); }
|
||||||
|
|
||||||
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
|
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
|
||||||
void invalidate_cut_info() { cut_info.invalidate(); }
|
void invalidate_cut_info() { cut_info.invalidate(); }
|
||||||
|
|
||||||
@ -846,7 +831,6 @@ public:
|
|||||||
bool is_the_only_one_part() const; // behave like an object
|
bool is_the_only_one_part() const; // behave like an object
|
||||||
t_model_material_id material_id() const { return m_material_id; }
|
t_model_material_id material_id() const { return m_material_id; }
|
||||||
void reset_extra_facets();
|
void reset_extra_facets();
|
||||||
void apply_tolerance();
|
|
||||||
void set_material_id(t_model_material_id material_id);
|
void set_material_id(t_model_material_id material_id);
|
||||||
ModelMaterial* material() const;
|
ModelMaterial* material() const;
|
||||||
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
||||||
|
@ -678,7 +678,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||||||
void PresetBundle::export_selections(AppConfig &config)
|
void PresetBundle::export_selections(AppConfig &config)
|
||||||
{
|
{
|
||||||
assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() >= 1);
|
assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() >= 1);
|
||||||
assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias);
|
// #ysFIXME_delete_after_test !All filament selections are always saved in extruder_filaments (for MM and SM printers),
|
||||||
|
// so there is no need to control a correspondence between filaments and extruders_filaments
|
||||||
|
//assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias);
|
||||||
config.clear_section("presets");
|
config.clear_section("presets");
|
||||||
config.set("presets", "print", prints.get_selected_preset_name());
|
config.set("presets", "print", prints.get_selected_preset_name());
|
||||||
config.set("presets", "filament", extruders_filaments.front().get_selected_preset_name());
|
config.set("presets", "filament", extruders_filaments.front().get_selected_preset_name());
|
||||||
|
@ -1539,21 +1539,33 @@ void PrintObject::discover_vertical_shells()
|
|||||||
// Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
|
// Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
|
||||||
narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare);
|
narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare);
|
||||||
|
|
||||||
|
Polygons object_volume;
|
||||||
Polygons internal_volume;
|
Polygons internal_volume;
|
||||||
{
|
{
|
||||||
Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{};
|
Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{};
|
||||||
Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ?
|
Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ?
|
||||||
to_polygons(m_layers[idx_layer + 1]->lslices) :
|
to_polygons(m_layers[idx_layer + 1]->lslices) :
|
||||||
Polygons{};
|
Polygons{};
|
||||||
internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice);
|
object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice);
|
||||||
|
internal_volume = closing(polygonsInternal, SCALED_EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The opening operation may cause scattered tiny drops on the smooth parts of the model, filter them out
|
// The regularization operation may cause scattered tiny drops on the smooth parts of the model, filter them out
|
||||||
|
// If the region checks both following conditions, it is removed:
|
||||||
|
// 1. the area is very small,
|
||||||
|
// OR the area is quite small and it is fully wrapped in model (not visible)
|
||||||
|
// the in-model condition is there due to small sloping surfaces, e.g. top of the hull of the benchy
|
||||||
|
// 2. the area does not fully cover an internal polygon
|
||||||
|
// This is there mainly for a very thin parts, where the solid layers would be missing if the part area is quite small
|
||||||
regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(),
|
regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(),
|
||||||
[&min_perimeter_infill_spacing, &internal_volume](const ExPolygon &p) {
|
[&internal_volume, &min_perimeter_infill_spacing,
|
||||||
return p.area() < min_perimeter_infill_spacing * scaled(1.5) ||
|
&object_volume](const ExPolygon &p) {
|
||||||
(p.area() < min_perimeter_infill_spacing * scaled(8.0) &&
|
return (p.area() < min_perimeter_infill_spacing * scaled(1.5) ||
|
||||||
diff(to_polygons(p), internal_volume).empty());
|
(p.area() < min_perimeter_infill_spacing * scaled(8.0) &&
|
||||||
|
diff(to_polygons(p), object_volume).empty())) &&
|
||||||
|
diff(internal_volume,
|
||||||
|
expand(to_polygons(p), min_perimeter_infill_spacing))
|
||||||
|
.size() >= internal_volume.size();
|
||||||
}),
|
}),
|
||||||
regularized_shell.end());
|
regularized_shell.end());
|
||||||
}
|
}
|
||||||
|
@ -1262,6 +1262,127 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion)
|
||||||
|
{
|
||||||
|
const float radius = (float)r;
|
||||||
|
const float height = (float)h;
|
||||||
|
const size_t sectors_cnt = 10; //(float)fa;
|
||||||
|
const float halfPI = 0.5f * (float)PI;
|
||||||
|
|
||||||
|
const float space_len = space_proportion * radius;
|
||||||
|
|
||||||
|
const float b_len = radius;
|
||||||
|
const float m_len = (1 + bulge_proportion) * radius;
|
||||||
|
const float t_len = 0.5f * radius;
|
||||||
|
|
||||||
|
const float b_height = 0.f;
|
||||||
|
const float m_height = 0.5f * height;
|
||||||
|
const float t_height = height;
|
||||||
|
|
||||||
|
const float b_angle = acos(space_len/b_len);
|
||||||
|
const float t_angle = acos(space_len/t_len);
|
||||||
|
|
||||||
|
const float b_angle_step = b_angle / (float)sectors_cnt;
|
||||||
|
const float t_angle_step = t_angle / (float)sectors_cnt;
|
||||||
|
|
||||||
|
const Vec2f b_vec = Eigen::Vector2f(0, b_len);
|
||||||
|
const Vec2f t_vec = Eigen::Vector2f(0, t_len);
|
||||||
|
|
||||||
|
|
||||||
|
auto add_side_vertices = [b_vec, t_vec, b_height, m_height, t_height](std::vector<stl_vertex>& vertices, float b_angle, float t_angle, const Vec2f& m_vec) {
|
||||||
|
Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec;
|
||||||
|
Vec2f m_pt = Eigen::Rotation2Df(b_angle) * m_vec;
|
||||||
|
Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec;
|
||||||
|
|
||||||
|
vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height));
|
||||||
|
vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height));
|
||||||
|
vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto add_side_facets = [](std::vector<stl_triangle_vertex_indices>& facets, int vertices_cnt, int frst_id, int scnd_id) {
|
||||||
|
int id = vertices_cnt - 1;
|
||||||
|
|
||||||
|
facets.emplace_back(frst_id, id - 2, id - 5);
|
||||||
|
|
||||||
|
facets.emplace_back(id - 2, id - 1, id - 5);
|
||||||
|
facets.emplace_back(id - 1, id - 4, id - 5);
|
||||||
|
facets.emplace_back(id - 4, id - 1, id);
|
||||||
|
facets.emplace_back(id, id - 3, id - 4);
|
||||||
|
|
||||||
|
facets.emplace_back(id, scnd_id, id - 3);
|
||||||
|
};
|
||||||
|
|
||||||
|
const float f = (b_len - m_len) / m_len; // Flattening
|
||||||
|
|
||||||
|
auto get_m_len = [b_len, f](float angle) {
|
||||||
|
const float rad_sqr = b_len * b_len;
|
||||||
|
const float sin_sqr = sin(angle) * sin(angle);
|
||||||
|
const float f_sqr = (1-f)*(1-f);
|
||||||
|
return sqrtf(rad_sqr / (1 + (1 / f_sqr - 1) * sin_sqr));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto add_sub_mesh = [add_side_vertices, add_side_facets, get_m_len,
|
||||||
|
b_height, t_height, b_angle, t_angle, b_angle_step, t_angle_step]
|
||||||
|
(indexed_triangle_set& mesh, float center_x, float angle_rotation, int frst_vertex_id) {
|
||||||
|
auto& vertices = mesh.vertices;
|
||||||
|
auto& facets = mesh.indices;
|
||||||
|
|
||||||
|
// 2 special vertices, top and bottom center, rest are relative to this
|
||||||
|
vertices.emplace_back(Vec3f(center_x, 0.f, b_height));
|
||||||
|
vertices.emplace_back(Vec3f(center_x, 0.f, t_height));
|
||||||
|
|
||||||
|
float b_angle_start = angle_rotation - b_angle;
|
||||||
|
float t_angle_start = angle_rotation - t_angle;
|
||||||
|
const float b_angle_stop = angle_rotation + b_angle;
|
||||||
|
|
||||||
|
const int frst_id = frst_vertex_id;
|
||||||
|
const int scnd_id = frst_id + 1;
|
||||||
|
|
||||||
|
// add first side vertices and internal facets
|
||||||
|
{
|
||||||
|
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
|
||||||
|
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
|
||||||
|
|
||||||
|
int id = (int)vertices.size() - 1;
|
||||||
|
|
||||||
|
facets.emplace_back(frst_id, id - 2, id - 1);
|
||||||
|
facets.emplace_back(frst_id, id - 1, id);
|
||||||
|
facets.emplace_back(frst_id, id, scnd_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add d side vertices and facets
|
||||||
|
while (!is_approx(b_angle_start, b_angle_stop)) {
|
||||||
|
b_angle_start += b_angle_step;
|
||||||
|
t_angle_start += t_angle_step;
|
||||||
|
|
||||||
|
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
|
||||||
|
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
|
||||||
|
|
||||||
|
add_side_facets(facets, (int)vertices.size(), frst_id, scnd_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add last internal facets to close the mesh
|
||||||
|
{
|
||||||
|
int id = (int)vertices.size() - 1;
|
||||||
|
|
||||||
|
facets.emplace_back(frst_id, scnd_id, id);
|
||||||
|
facets.emplace_back(frst_id, id, id - 1);
|
||||||
|
facets.emplace_back(frst_id, id - 1, id - 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
indexed_triangle_set mesh;
|
||||||
|
|
||||||
|
mesh.vertices.reserve(2 * (3 * (2 * sectors_cnt + 1) + 2));
|
||||||
|
mesh.indices.reserve(2 * (6 * 2 * sectors_cnt + 6));
|
||||||
|
|
||||||
|
add_sub_mesh(mesh, -space_len, halfPI , 0);
|
||||||
|
add_sub_mesh(mesh, space_len, 3 * halfPI, (int)mesh.vertices.size());
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
|
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
|
||||||
{
|
{
|
||||||
std::vector<Vec3f> dst_vertices;
|
std::vector<Vec3f> dst_vertices;
|
||||||
|
@ -321,6 +321,7 @@ indexed_triangle_set its_make_frustum(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_frustum_dowel(double r, double h, int sectorCount);
|
||||||
indexed_triangle_set its_make_pyramid(float base, float height);
|
indexed_triangle_set its_make_pyramid(float base, float height);
|
||||||
indexed_triangle_set its_make_sphere(double radius, double fa);
|
indexed_triangle_set its_make_sphere(double radius, double fa);
|
||||||
|
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
|
||||||
|
|
||||||
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
|
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
|
||||||
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }
|
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }
|
||||||
|
@ -2757,6 +2757,11 @@ bool TickCodeInfo::add_tick(const int tick, Type type, const int extruder, doubl
|
|||||||
|
|
||||||
bool TickCodeInfo::edit_tick(std::set<TickCode>::iterator it, double print_z)
|
bool TickCodeInfo::edit_tick(std::set<TickCode>::iterator it, double print_z)
|
||||||
{
|
{
|
||||||
|
// Save previously value of the tick before the call a Dialog from get_... functions,
|
||||||
|
// otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close
|
||||||
|
// and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941)
|
||||||
|
TickCode changed_tick = *it;
|
||||||
|
|
||||||
std::string edited_value;
|
std::string edited_value;
|
||||||
if (it->type == ColorChange)
|
if (it->type == ColorChange)
|
||||||
edited_value = get_new_color(it->color);
|
edited_value = get_new_color(it->color);
|
||||||
@ -2768,7 +2773,10 @@ bool TickCodeInfo::edit_tick(std::set<TickCode>::iterator it, double print_z)
|
|||||||
if (edited_value.empty())
|
if (edited_value.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TickCode changed_tick = *it;
|
// Update iterator. For this moment its value can be invalid
|
||||||
|
if (it = ticks.find(changed_tick); it == ticks.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (it->type == ColorChange) {
|
if (it->type == ColorChange) {
|
||||||
if (it->color == edited_value)
|
if (it->color == edited_value)
|
||||||
return false;
|
return false;
|
||||||
|
@ -588,6 +588,8 @@ bool TextCtrl::value_was_changed()
|
|||||||
case coFloatOrPercent:
|
case coFloatOrPercent:
|
||||||
case coFloatsOrPercents:
|
case coFloatsOrPercents:
|
||||||
return boost::any_cast<std::string>(m_value) != boost::any_cast<std::string>(val);
|
return boost::any_cast<std::string>(m_value) != boost::any_cast<std::string>(val);
|
||||||
|
case coPoints:
|
||||||
|
return boost::any_cast<std::vector<Vec2d>>(m_value) != boost::any_cast<std::vector<Vec2d>>(val);
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2689,6 +2689,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
if (curr_gizmo != nullptr)
|
if (curr_gizmo != nullptr)
|
||||||
curr_gizmo->unregister_raycasters_for_picking();
|
curr_gizmo->unregister_raycasters_for_picking();
|
||||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo);
|
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo);
|
||||||
|
if (curr_gizmo != nullptr && !m_selection.is_empty())
|
||||||
|
curr_gizmo->register_raycasters_for_picking();
|
||||||
|
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::FallbackGizmo);
|
||||||
if (curr_gizmo != nullptr && !m_selection.is_empty())
|
if (curr_gizmo != nullptr && !m_selection.is_empty())
|
||||||
curr_gizmo->register_raycasters_for_picking();
|
curr_gizmo->register_raycasters_for_picking();
|
||||||
|
|
||||||
@ -3597,11 +3600,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
update_sequential_clearance(true);
|
update_sequential_clearance(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (evt.LeftUp() &&
|
|
||||||
m_gizmos.get_current_type() == GLGizmosManager::EType::Scale &&
|
|
||||||
m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) {
|
|
||||||
wxGetApp().obj_list()->selection_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4071,7 +4069,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||||||
model_object->invalidate_bounding_box();
|
model_object->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (v->is_wipe_tower)
|
else if (m_selection.is_wipe_tower() && v->is_wipe_tower)
|
||||||
// Move a wipe tower proxy.
|
// Move a wipe tower proxy.
|
||||||
wipe_tower_origin = v->get_volume_offset();
|
wipe_tower_origin = v->get_volume_offset();
|
||||||
}
|
}
|
||||||
@ -5749,6 +5747,7 @@ void GLCanvas3D::_picking_pass()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SceneRaycaster::EType::Gizmo:
|
case SceneRaycaster::EType::Gizmo:
|
||||||
|
case SceneRaycaster::EType::FallbackGizmo:
|
||||||
{
|
{
|
||||||
const Size& cnv_size = get_canvas_size();
|
const Size& cnv_size = get_canvas_size();
|
||||||
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
||||||
@ -5781,6 +5780,7 @@ void GLCanvas3D::_picking_pass()
|
|||||||
{
|
{
|
||||||
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
|
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
|
||||||
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
|
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
|
||||||
|
case SceneRaycaster::EType::FallbackGizmo: { object_type = "Gizmo2 element"; break; }
|
||||||
case SceneRaycaster::EType::Volume:
|
case SceneRaycaster::EType::Volume:
|
||||||
{
|
{
|
||||||
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
|
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
|
||||||
@ -5835,6 +5835,8 @@ void GLCanvas3D::_picking_pass()
|
|||||||
add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||||
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count());
|
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count());
|
||||||
add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||||
|
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.fallback_gizmos_count(), (int)m_scene_raycaster.active_fallback_gizmos_count());
|
||||||
|
add_strings_row_to_table("Gizmo2 elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5852,6 +5854,20 @@ void GLCanvas3D::_picking_pass()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<SceneRaycasterItem>>* gizmo2_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::FallbackGizmo);
|
||||||
|
if (gizmo2_raycasters != nullptr && !gizmo2_raycasters->empty()) {
|
||||||
|
ImGui::Separator();
|
||||||
|
imgui.text("Gizmo2 raycasters IDs:");
|
||||||
|
if (ImGui::BeginTable("Gizmo2Raycasters", 3)) {
|
||||||
|
for (size_t i = 0; i < gizmo2_raycasters->size(); ++i) {
|
||||||
|
add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||||
|
std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::FallbackGizmo, (*gizmo2_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text),
|
||||||
|
to_string(Geometry::Transformation((*gizmo2_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
imgui.end();
|
imgui.end();
|
||||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||||
}
|
}
|
||||||
|
@ -503,6 +503,11 @@ void ObjectManipulation::Show(const bool show)
|
|||||||
}
|
}
|
||||||
m_word_local_combo->Show(show_world_local_combo);
|
m_word_local_combo->Show(show_world_local_combo);
|
||||||
m_empty_str->Show(!show_world_local_combo);
|
m_empty_str->Show(!show_world_local_combo);
|
||||||
|
|
||||||
|
m_skew_label->Show(m_show_skew);
|
||||||
|
m_reset_skew_button->Show(m_show_skew);
|
||||||
|
|
||||||
|
m_parent->Layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,15 +800,22 @@ void ObjectManipulation::update_reset_buttons_visibility()
|
|||||||
m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap);
|
||||||
m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0));
|
||||||
m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : "");
|
m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : "");
|
||||||
m_reset_skew_button->Show(show_skew);
|
|
||||||
m_skew_label->Show(show_skew);
|
|
||||||
|
|
||||||
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
if (m_show_skew == show_skew)
|
||||||
Sidebar& panel = wxGetApp().sidebar();
|
get_sizer()->Layout();
|
||||||
if (!panel.IsFrozen()) {
|
else {
|
||||||
panel.Freeze();
|
// Call sidebar layout only if it's really needed,
|
||||||
panel.Layout();
|
// it means, when we show/hide additional line for skew information
|
||||||
panel.Thaw();
|
m_show_skew = show_skew;
|
||||||
|
m_reset_skew_button->Show(m_show_skew);
|
||||||
|
m_skew_label->Show(m_show_skew);
|
||||||
|
// Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time
|
||||||
|
Sidebar& panel = wxGetApp().sidebar();
|
||||||
|
if (!panel.IsFrozen()) {
|
||||||
|
panel.Freeze();
|
||||||
|
panel.Layout();
|
||||||
|
panel.Thaw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ private:
|
|||||||
bool m_is_enabled { true };
|
bool m_is_enabled { true };
|
||||||
bool m_is_enabled_size_and_scale { true };
|
bool m_is_enabled_size_and_scale { true };
|
||||||
|
|
||||||
|
bool m_show_skew { false };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectManipulation(wxWindow* parent);
|
ObjectManipulation(wxWindow* parent);
|
||||||
|
@ -135,6 +135,11 @@ GalleryDialog::GalleryDialog(wxWindow* parent) :
|
|||||||
|
|
||||||
GalleryDialog::~GalleryDialog()
|
GalleryDialog::~GalleryDialog()
|
||||||
{
|
{
|
||||||
|
// From wxWidgets docs:
|
||||||
|
// The method void wxListCtrl::SetImageList(wxImageList* imageList, int which)
|
||||||
|
// does not take ownership of the image list, you have to delete it yourself.
|
||||||
|
if (m_image_list)
|
||||||
|
delete m_image_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GalleryDialog::show(bool show_from_menu)
|
int GalleryDialog::show(bool show_from_menu)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,14 @@
|
|||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/CutUtils.hpp"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum class CutConnectorType : int;
|
enum class CutConnectorType : int;
|
||||||
class ModelVolume;
|
class ModelVolume;
|
||||||
|
class GLShaderProgram;
|
||||||
struct CutConnectorAttributes;
|
struct CutConnectorAttributes;
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
@ -29,6 +31,9 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
Y,
|
Y,
|
||||||
Z,
|
Z,
|
||||||
CutPlane,
|
CutPlane,
|
||||||
|
CutPlaneZRotation,
|
||||||
|
CutPlaneXMove,
|
||||||
|
CutPlaneYMove,
|
||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,6 +59,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
double m_radius{ 0.0 };
|
double m_radius{ 0.0 };
|
||||||
double m_grabber_radius{ 0.0 };
|
double m_grabber_radius{ 0.0 };
|
||||||
double m_grabber_connection_len{ 0.0 };
|
double m_grabber_connection_len{ 0.0 };
|
||||||
|
Vec3d m_cut_plane_start_move_pos {Vec3d::Zero()};
|
||||||
|
|
||||||
double m_snap_coarse_in_radius{ 0.0 };
|
double m_snap_coarse_in_radius{ 0.0 };
|
||||||
double m_snap_coarse_out_radius{ 0.0 };
|
double m_snap_coarse_out_radius{ 0.0 };
|
||||||
@ -78,6 +84,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
PickingModel m_plane;
|
PickingModel m_plane;
|
||||||
PickingModel m_sphere;
|
PickingModel m_sphere;
|
||||||
PickingModel m_cone;
|
PickingModel m_cone;
|
||||||
|
PickingModel m_cube;
|
||||||
std::map<CutConnectorAttributes, PickingModel> m_shapes;
|
std::map<CutConnectorAttributes, PickingModel> m_shapes;
|
||||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
|
std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
|
||||||
|
|
||||||
@ -111,6 +118,16 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
bool m_rotate_upper{ false };
|
bool m_rotate_upper{ false };
|
||||||
bool m_rotate_lower{ false };
|
bool m_rotate_lower{ false };
|
||||||
|
|
||||||
|
// Input params for cut with tongue and groove
|
||||||
|
Cut::Groove m_groove;
|
||||||
|
bool m_groove_editing { false };
|
||||||
|
|
||||||
|
bool m_is_slider_editing_done { false };
|
||||||
|
|
||||||
|
// Input params for cut with snaps
|
||||||
|
float m_snap_bulge_proportion{ 0.15f };
|
||||||
|
float m_snap_space_proportion{ 0.3f };
|
||||||
|
|
||||||
bool m_hide_cut_plane{ false };
|
bool m_hide_cut_plane{ false };
|
||||||
bool m_connectors_editing{ false };
|
bool m_connectors_editing{ false };
|
||||||
bool m_cut_plane_as_circle{ false };
|
bool m_cut_plane_as_circle{ false };
|
||||||
@ -127,7 +144,6 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
|
|
||||||
float m_contour_width{ 0.4f };
|
float m_contour_width{ 0.4f };
|
||||||
float m_cut_plane_radius_koef{ 1.5f };
|
float m_cut_plane_radius_koef{ 1.5f };
|
||||||
bool m_is_contour_changed{ false };
|
|
||||||
float m_shortcut_label_width{ -1.f };
|
float m_shortcut_label_width{ -1.f };
|
||||||
|
|
||||||
mutable std::vector<bool> m_selected; // which pins are currently selected
|
mutable std::vector<bool> m_selected; // which pins are currently selected
|
||||||
@ -139,10 +155,14 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
bool m_was_cut_plane_dragged { false };
|
bool m_was_cut_plane_dragged { false };
|
||||||
bool m_was_contour_selected { false };
|
bool m_was_contour_selected { false };
|
||||||
|
|
||||||
|
// Vertices of the groove used to detection if groove is valid
|
||||||
|
std::vector<Vec3d> m_groove_vertices;
|
||||||
|
|
||||||
class PartSelection {
|
class PartSelection {
|
||||||
public:
|
public:
|
||||||
PartSelection() = default;
|
PartSelection() = default;
|
||||||
PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc);
|
PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc);
|
||||||
|
PartSelection(const ModelObject* mo, int instance_idx_in);
|
||||||
~PartSelection() { m_model.clear_objects(); }
|
~PartSelection() { m_model.clear_objects(); }
|
||||||
|
|
||||||
struct Part {
|
struct Part {
|
||||||
@ -161,6 +181,8 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
const std::vector<Part>& parts() const { return m_parts; }
|
const std::vector<Part>& parts() const { return m_parts; }
|
||||||
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
|
||||||
|
|
||||||
|
std::vector<Cut::Part> get_cut_parts();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model m_model;
|
Model m_model;
|
||||||
int m_instance_idx;
|
int m_instance_idx;
|
||||||
@ -171,6 +193,8 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
|
|
||||||
std::vector<Vec3d> m_contour_points; // Debugging
|
std::vector<Vec3d> m_contour_points; // Debugging
|
||||||
std::vector<std::vector<Vec3d>> m_debug_pts; // Debugging
|
std::vector<std::vector<Vec3d>> m_debug_pts; // Debugging
|
||||||
|
|
||||||
|
void add_object(const ModelObject* object);
|
||||||
};
|
};
|
||||||
|
|
||||||
PartSelection m_part_selection;
|
PartSelection m_part_selection;
|
||||||
@ -180,7 +204,8 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
|
|
||||||
enum class CutMode {
|
enum class CutMode {
|
||||||
cutPlanar
|
cutPlanar
|
||||||
, cutGrig
|
, cutTongueAndGroove
|
||||||
|
//, cutGrig
|
||||||
//,cutRadial
|
//,cutRadial
|
||||||
//,cutModular
|
//,cutModular
|
||||||
};
|
};
|
||||||
@ -190,7 +215,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||||||
, Manual
|
, Manual
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::vector<std::string> m_modes;
|
std::vector<std::string> m_modes;
|
||||||
size_t m_mode{ size_t(CutMode::cutPlanar) };
|
size_t m_mode{ size_t(CutMode::cutPlanar) };
|
||||||
|
|
||||||
std::vector<std::string> m_connector_modes;
|
std::vector<std::string> m_connector_modes;
|
||||||
@ -215,7 +240,7 @@ public:
|
|||||||
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||||
|
|
||||||
std::string get_tooltip() const override;
|
std::string get_tooltip() const override;
|
||||||
bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_disabled_contour = true);
|
bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_contours = true);
|
||||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||||
|
|
||||||
bool is_in_editing_mode() const override { return m_connectors_editing; }
|
bool is_in_editing_mode() const override { return m_connectors_editing; }
|
||||||
@ -249,8 +274,8 @@ protected:
|
|||||||
bool on_is_activable() const override;
|
bool on_is_activable() const override;
|
||||||
bool on_is_selectable() const override;
|
bool on_is_selectable() const override;
|
||||||
Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
|
Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
|
||||||
void dragging_grabber_z(const GLGizmoBase::UpdateData &data);
|
void dragging_grabber_move(const GLGizmoBase::UpdateData &data);
|
||||||
void dragging_grabber_xy(const GLGizmoBase::UpdateData &data);
|
void dragging_grabber_rotation(const GLGizmoBase::UpdateData &data);
|
||||||
void dragging_connector(const GLGizmoBase::UpdateData &data);
|
void dragging_connector(const GLGizmoBase::UpdateData &data);
|
||||||
void on_dragging(const UpdateData&data) override;
|
void on_dragging(const UpdateData&data) override;
|
||||||
void on_start_dragging() override;
|
void on_start_dragging() override;
|
||||||
@ -275,6 +300,8 @@ protected:
|
|||||||
void add_horizontal_scaled_interval(float interval);
|
void add_horizontal_scaled_interval(float interval);
|
||||||
void add_horizontal_shift(float shift);
|
void add_horizontal_shift(float shift);
|
||||||
void render_color_marker(float size, const ImU32& color);
|
void render_color_marker(float size, const ImU32& color);
|
||||||
|
void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance);
|
||||||
|
void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val);
|
||||||
void render_cut_plane_input_window(CutConnectors &connectors);
|
void render_cut_plane_input_window(CutConnectors &connectors);
|
||||||
void init_input_window_data(CutConnectors &connectors);
|
void init_input_window_data(CutConnectors &connectors);
|
||||||
void render_input_window_warning() const;
|
void render_input_window_warning() const;
|
||||||
@ -290,6 +317,8 @@ protected:
|
|||||||
void set_volumes_picking_state(bool state);
|
void set_volumes_picking_state(bool state);
|
||||||
void update_raycasters_for_picking_transform();
|
void update_raycasters_for_picking_transform();
|
||||||
|
|
||||||
|
void update_plane_model();
|
||||||
|
|
||||||
void on_render_input_window(float x, float y, float bottom_limit) override;
|
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||||
|
|
||||||
bool wants_enter_leave_snapshots() const override { return true; }
|
bool wants_enter_leave_snapshots() const override { return true; }
|
||||||
@ -301,10 +330,12 @@ protected:
|
|||||||
Transform3d get_cut_matrix(const Selection& selection);
|
Transform3d get_cut_matrix(const Selection& selection);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_center(const Vec3d& center, bool update_tbb = false);
|
void set_center(const Vec3d¢er, bool update_tbb = false);
|
||||||
bool render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx);
|
void switch_to_mode(size_t new_mode);
|
||||||
|
bool render_cut_mode_combo();
|
||||||
|
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
|
||||||
bool render_double_input(const std::string& label, double& value_in);
|
bool render_double_input(const std::string& label, double& value_in);
|
||||||
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in);
|
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
|
||||||
void render_move_center_input(int axis);
|
void render_move_center_input(int axis);
|
||||||
void render_connect_mode_radio_button(CutConnectorMode mode);
|
void render_connect_mode_radio_button(CutConnectorMode mode);
|
||||||
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
||||||
@ -314,16 +345,18 @@ private:
|
|||||||
void render_connectors();
|
void render_connectors();
|
||||||
|
|
||||||
bool can_perform_cut() const;
|
bool can_perform_cut() const;
|
||||||
|
bool has_valid_groove() const;
|
||||||
bool has_valid_contour() const;
|
bool has_valid_contour() const;
|
||||||
void apply_connectors_in_model(ModelObject* mo, int &dowels_count);
|
void apply_connectors_in_model(ModelObject* mo, int &dowels_count);
|
||||||
bool cut_line_processing() const;
|
bool cut_line_processing() const;
|
||||||
void discard_cut_line_processing();
|
void discard_cut_line_processing();
|
||||||
|
|
||||||
|
void apply_color_clip_plane_colors();
|
||||||
void render_cut_plane();
|
void render_cut_plane();
|
||||||
static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
|
static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
|
||||||
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
|
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
|
||||||
void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
|
void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
|
||||||
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix);
|
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0);
|
||||||
void render_cut_plane_grabbers();
|
void render_cut_plane_grabbers();
|
||||||
void render_cut_line();
|
void render_cut_line();
|
||||||
void perform_cut(const Selection&selection);
|
void perform_cut(const Selection&selection);
|
||||||
@ -339,6 +372,13 @@ private:
|
|||||||
void validate_connector_settings();
|
void validate_connector_settings();
|
||||||
bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position);
|
bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position);
|
||||||
void check_and_update_connectors_state();
|
void check_and_update_connectors_state();
|
||||||
|
|
||||||
|
void toggle_model_objects_visibility();
|
||||||
|
|
||||||
|
indexed_triangle_set its_make_groove_plane();
|
||||||
|
|
||||||
|
indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
|
||||||
|
void apply_cut_connectors(ModelObject* mo, const std::string& connector_name);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
|
@ -99,6 +99,12 @@ void GLGizmoScale3D::enable_ununiversal_scale(bool enable)
|
|||||||
m_grabbers[i].enabled = enable;
|
m_grabbers[i].enabled = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLGizmoScale3D::on_set_state()
|
||||||
|
{
|
||||||
|
if (m_state == On)
|
||||||
|
wxGetApp().obj_list()->selection_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoScale3D::data_changed(bool is_serializing) {
|
void GLGizmoScale3D::data_changed(bool is_serializing) {
|
||||||
set_scale(Vec3d::Ones());
|
set_scale(Vec3d::Ones());
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,8 @@ protected:
|
|||||||
virtual void on_register_raycasters_for_picking() override;
|
virtual void on_register_raycasters_for_picking() override;
|
||||||
virtual void on_unregister_raycasters_for_picking() override;
|
virtual void on_unregister_raycasters_for_picking() override;
|
||||||
|
|
||||||
|
void on_set_state() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color);
|
void render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color);
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||||||
{ImGui::InfoMarkerSmall , "notification_info" },
|
{ImGui::InfoMarkerSmall , "notification_info" },
|
||||||
{ImGui::PlugMarker , "plug" },
|
{ImGui::PlugMarker , "plug" },
|
||||||
{ImGui::DowelMarker , "dowel" },
|
{ImGui::DowelMarker , "dowel" },
|
||||||
|
{ImGui::SnapMarker , "snap" },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||||
@ -639,6 +640,8 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
|
|||||||
m_last_slider_status.edited = ImGui::IsItemEdited();
|
m_last_slider_status.edited = ImGui::IsItemEdited();
|
||||||
m_last_slider_status.clicked = ImGui::IsItemClicked();
|
m_last_slider_status.clicked = ImGui::IsItemClicked();
|
||||||
m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit();
|
m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit();
|
||||||
|
if (!m_last_slider_status.can_take_snapshot)
|
||||||
|
m_last_slider_status.can_take_snapshot = ImGui::IsItemClicked();
|
||||||
|
|
||||||
if (!tooltip.empty() && ImGui::IsItemHovered())
|
if (!tooltip.empty() && ImGui::IsItemHovered())
|
||||||
this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width);
|
this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width);
|
||||||
|
@ -49,6 +49,11 @@ public:
|
|||||||
bool edited { false };
|
bool edited { false };
|
||||||
bool clicked { false };
|
bool clicked { false };
|
||||||
bool deactivated_after_edit { false };
|
bool deactivated_after_edit { false };
|
||||||
|
// flag to indicate possibility to take snapshot from the slider value
|
||||||
|
// It's used from Gizmos to take snapshots just from the very beginning of the editing
|
||||||
|
bool can_take_snapshot { false };
|
||||||
|
// When Undo/Redo snapshot is taken, then call this function
|
||||||
|
void invalidate_snapshot() { can_take_snapshot = false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
ImGuiWrapper();
|
ImGuiWrapper();
|
||||||
@ -80,6 +85,7 @@ public:
|
|||||||
ImVec2 get_item_spacing() const;
|
ImVec2 get_item_spacing() const;
|
||||||
float get_slider_float_height() const;
|
float get_slider_float_height() const;
|
||||||
const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; }
|
const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; }
|
||||||
|
LastSliderStatus& get_last_slider_status() { return m_last_slider_status; }
|
||||||
|
|
||||||
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
||||||
void set_next_window_bg_alpha(float alpha);
|
void set_next_window_bg_alpha(float alpha);
|
||||||
|
@ -465,14 +465,17 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const
|
bool MeshRaycaster::intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const
|
||||||
{
|
{
|
||||||
point = trafo.inverse() * point;
|
Transform3d trafo_inv = trafo.inverse();
|
||||||
|
Vec3d to = trafo_inv * (point + direction);
|
||||||
|
point = trafo_inv * point;
|
||||||
|
direction = (to-point).normalized();
|
||||||
|
|
||||||
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
|
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
|
||||||
std::vector<AABBMesh::hit_result> neg_hits = m_emesh.query_ray_hits(point, -direction);
|
std::vector<AABBMesh::hit_result> neg_hits = m_emesh.query_ray_hits(point, -direction);
|
||||||
|
|
||||||
return !hits.empty() && !neg_hits.empty();
|
return !hits.empty() || !neg_hits.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -187,7 +187,9 @@ public:
|
|||||||
|
|
||||||
const AABBMesh &get_aabb_mesh() const { return m_emesh; }
|
const AABBMesh &get_aabb_mesh() const { return m_emesh; }
|
||||||
|
|
||||||
bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const;
|
// Given a point and direction in world coords, returns whether the respective line
|
||||||
|
// intersects the mesh if it is transformed into world by trafo.
|
||||||
|
bool intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const;
|
||||||
|
|
||||||
// Given a vector of points in woorld coordinates, this returns vector
|
// Given a vector of points in woorld coordinates, this returns vector
|
||||||
// of indices of points that are visible (i.e. not cut by clipping plane
|
// of indices of points that are visible (i.e. not cut by clipping plane
|
||||||
|
@ -1280,9 +1280,11 @@ void Sidebar::show_info_sizer()
|
|||||||
{
|
{
|
||||||
Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
ModelObjectPtrs objects = p->plater->model().objects;
|
ModelObjectPtrs objects = p->plater->model().objects;
|
||||||
int obj_idx = selection.get_object_idx();
|
const int obj_idx = selection.get_object_idx();
|
||||||
|
const int inst_idx = selection.get_instance_idx();
|
||||||
|
|
||||||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx ||
|
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx ||
|
||||||
|
inst_idx < 0 || int(objects[obj_idx]->instances.size()) <= inst_idx ||
|
||||||
objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed
|
objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed
|
||||||
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
||||||
!(selection.is_single_full_instance() || selection.is_single_volume())) {
|
!(selection.is_single_full_instance() || selection.is_single_volume())) {
|
||||||
@ -1292,9 +1294,6 @@ void Sidebar::show_info_sizer()
|
|||||||
|
|
||||||
const ModelObject* model_object = objects[obj_idx];
|
const ModelObject* model_object = objects[obj_idx];
|
||||||
|
|
||||||
int inst_idx = selection.get_instance_idx();
|
|
||||||
assert(inst_idx >= 0);
|
|
||||||
|
|
||||||
bool imperial_units = wxGetApp().app_config->get_bool("use_inches");
|
bool imperial_units = wxGetApp().app_config->get_bool("use_inches");
|
||||||
double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f;
|
double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f;
|
||||||
|
|
||||||
@ -6417,20 +6416,7 @@ void Plater::toggle_layers_editing(bool enable)
|
|||||||
canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting"));
|
canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes)
|
void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects)
|
||||||
{
|
|
||||||
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
|
|
||||||
auto* object = p->model.objects[obj_idx];
|
|
||||||
|
|
||||||
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
|
|
||||||
|
|
||||||
wxBusyCursor wait;
|
|
||||||
|
|
||||||
const auto new_objects = object->cut(instance_idx, cut_matrix, attributes);
|
|
||||||
cut(obj_idx, new_objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::cut(size_t obj_idx, const ModelObjectPtrs& new_objects)
|
|
||||||
{
|
{
|
||||||
model().delete_object(obj_idx);
|
model().delete_object(obj_idx);
|
||||||
sidebar().obj_list()->delete_object_from_list(obj_idx);
|
sidebar().obj_list()->delete_object_from_list(obj_idx);
|
||||||
@ -7232,7 +7218,9 @@ void Plater::force_filament_cb_update()
|
|||||||
|
|
||||||
// Update preset comboboxes on sidebar and filaments tab
|
// Update preset comboboxes on sidebar and filaments tab
|
||||||
p->sidebar->update_presets(Preset::TYPE_FILAMENT);
|
p->sidebar->update_presets(Preset::TYPE_FILAMENT);
|
||||||
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filaments.get_selected_preset_name());
|
|
||||||
|
TabFilament* tab = dynamic_cast<TabFilament*>(wxGetApp().get_tab(Preset::TYPE_FILAMENT));
|
||||||
|
tab->select_preset(wxGetApp().preset_bundle->extruders_filaments[tab->get_active_extruder()].get_selected_preset_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::force_print_bed_update()
|
void Plater::force_print_bed_update()
|
||||||
|
@ -268,8 +268,7 @@ public:
|
|||||||
void convert_unit(ConversionType conv_type);
|
void convert_unit(ConversionType conv_type);
|
||||||
void toggle_layers_editing(bool enable);
|
void toggle_layers_editing(bool enable);
|
||||||
|
|
||||||
void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes);
|
void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
|
||||||
void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects);
|
|
||||||
|
|
||||||
void export_gcode(bool prefer_removable);
|
void export_gcode(bool prefer_removable);
|
||||||
void export_stl_obj(bool extended = false, bool selection_only = false);
|
void export_stl_obj(bool extended = false, bool selection_only = false);
|
||||||
|
@ -40,6 +40,7 @@ std::shared_ptr<SceneRaycasterItem> SceneRaycaster::add_raycaster(EType type, in
|
|||||||
case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||||
case EType::Volume: { return m_volumes.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
case EType::Volume: { return m_volumes.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||||
case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||||
|
case EType::FallbackGizmo: { return m_fallback_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||||
default: { assert(false); return nullptr; }
|
default: { assert(false); return nullptr; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -62,6 +63,7 @@ void SceneRaycaster::remove_raycasters(EType type)
|
|||||||
case EType::Bed: { m_bed.clear(); break; }
|
case EType::Bed: { m_bed.clear(); break; }
|
||||||
case EType::Volume: { m_volumes.clear(); break; }
|
case EType::Volume: { m_volumes.clear(); break; }
|
||||||
case EType::Gizmo: { m_gizmos.clear(); break; }
|
case EType::Gizmo: { m_gizmos.clear(); break; }
|
||||||
|
case EType::FallbackGizmo: { m_fallback_gizmos.clear(); break; }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -86,6 +88,12 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr<SceneRaycasterItem> item)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto it = m_fallback_gizmos.begin(); it != m_fallback_gizmos.end(); ++it) {
|
||||||
|
if (*it == item) {
|
||||||
|
m_fallback_gizmos.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
|
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
|
||||||
@ -174,6 +182,9 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
|||||||
if (!m_gizmos.empty())
|
if (!m_gizmos.empty())
|
||||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||||
|
|
||||||
|
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||||
|
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||||
|
|
||||||
if (!m_gizmos_on_top || !ret.is_valid()) {
|
if (!m_gizmos_on_top || !ret.is_valid()) {
|
||||||
if (camera.is_looking_downward() && !m_bed.empty())
|
if (camera.is_looking_downward() && !m_bed.empty())
|
||||||
test_raycasters(EType::Bed, mouse_pos, camera, ret);
|
test_raycasters(EType::Bed, mouse_pos, camera, ret);
|
||||||
@ -241,6 +252,14 @@ size_t SceneRaycaster::active_gizmos_count() const {
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
size_t SceneRaycaster::active_fallback_gizmos_count() const {
|
||||||
|
size_t count = 0;
|
||||||
|
for (const auto& g : m_fallback_gizmos) {
|
||||||
|
if (g->is_active())
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||||
|
|
||||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type)
|
std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type)
|
||||||
@ -251,6 +270,7 @@ std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters
|
|||||||
case EType::Bed: { ret = &m_bed; break; }
|
case EType::Bed: { ret = &m_bed; break; }
|
||||||
case EType::Volume: { ret = &m_volumes; break; }
|
case EType::Volume: { ret = &m_volumes; break; }
|
||||||
case EType::Gizmo: { ret = &m_gizmos; break; }
|
case EType::Gizmo: { ret = &m_gizmos; break; }
|
||||||
|
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
}
|
}
|
||||||
assert(ret != nullptr);
|
assert(ret != nullptr);
|
||||||
@ -265,6 +285,7 @@ const std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_rayc
|
|||||||
case EType::Bed: { ret = &m_bed; break; }
|
case EType::Bed: { ret = &m_bed; break; }
|
||||||
case EType::Volume: { ret = &m_volumes; break; }
|
case EType::Volume: { ret = &m_volumes; break; }
|
||||||
case EType::Gizmo: { ret = &m_gizmos; break; }
|
case EType::Gizmo: { ret = &m_gizmos; break; }
|
||||||
|
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
}
|
}
|
||||||
assert(ret != nullptr);
|
assert(ret != nullptr);
|
||||||
@ -278,6 +299,7 @@ int SceneRaycaster::base_id(EType type)
|
|||||||
case EType::Bed: { return int(EIdBase::Bed); }
|
case EType::Bed: { return int(EIdBase::Bed); }
|
||||||
case EType::Volume: { return int(EIdBase::Volume); }
|
case EType::Volume: { return int(EIdBase::Volume); }
|
||||||
case EType::Gizmo: { return int(EIdBase::Gizmo); }
|
case EType::Gizmo: { return int(EIdBase::Gizmo); }
|
||||||
|
case EType::FallbackGizmo: { return int(EIdBase::FallbackGizmo); }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,14 +42,16 @@ public:
|
|||||||
None,
|
None,
|
||||||
Bed,
|
Bed,
|
||||||
Volume,
|
Volume,
|
||||||
Gizmo
|
Gizmo,
|
||||||
|
FallbackGizmo // Is used for gizmo grabbers which will be hit after all grabbers of Gizmo type
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EIdBase
|
enum class EIdBase
|
||||||
{
|
{
|
||||||
Bed = 0,
|
Bed = 0,
|
||||||
Volume = 1000,
|
Volume = 1000,
|
||||||
Gizmo = 1000000
|
Gizmo = 1000000,
|
||||||
|
FallbackGizmo = 2000000
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HitResult
|
struct HitResult
|
||||||
@ -66,6 +68,7 @@ private:
|
|||||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_bed;
|
std::vector<std::shared_ptr<SceneRaycasterItem>> m_bed;
|
||||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volumes;
|
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volumes;
|
||||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_gizmos;
|
std::vector<std::shared_ptr<SceneRaycasterItem>> m_gizmos;
|
||||||
|
std::vector<std::shared_ptr<SceneRaycasterItem>> m_fallback_gizmos;
|
||||||
|
|
||||||
// When set to true, if checking gizmos returns a valid hit,
|
// When set to true, if checking gizmos returns a valid hit,
|
||||||
// the search is not performed on other types
|
// the search is not performed on other types
|
||||||
@ -99,9 +102,11 @@ public:
|
|||||||
size_t beds_count() const { return m_bed.size(); }
|
size_t beds_count() const { return m_bed.size(); }
|
||||||
size_t volumes_count() const { return m_volumes.size(); }
|
size_t volumes_count() const { return m_volumes.size(); }
|
||||||
size_t gizmos_count() const { return m_gizmos.size(); }
|
size_t gizmos_count() const { return m_gizmos.size(); }
|
||||||
|
size_t fallback_gizmos_count() const { return m_fallback_gizmos.size(); }
|
||||||
size_t active_beds_count() const;
|
size_t active_beds_count() const;
|
||||||
size_t active_volumes_count() const;
|
size_t active_volumes_count() const;
|
||||||
size_t active_gizmos_count() const;
|
size_t active_gizmos_count() const;
|
||||||
|
size_t active_fallback_gizmos_count() const;
|
||||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||||
|
|
||||||
static int decode_id(EType type, int id);
|
static int decode_id(EType type, int id);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user