Comparar commits
108 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 89a8fd8b4f | |||
| c73cf897ac | |||
| 43408040c7 | |||
| 0cd3c7829b | |||
| 05c4f3da01 | |||
| 44631f3a6f | |||
| 1ca622b048 | |||
| 17e02edff7 | |||
| 563317f47d | |||
| 4b3a4f57a1 | |||
| 0f15b885f2 | |||
| ecceb71fdd | |||
| b8d019ba4a | |||
| 310eba6e16 | |||
| 82c8ac78a5 | |||
| 60e5b6088e | |||
| f8d17c09bf | |||
| 72605c9d51 | |||
| dad342fefd | |||
| 5b0c3fd4aa | |||
| 7693630f5f | |||
| e4a23e2bef | |||
| f5e3cf4c91 | |||
| 22013f70ab | |||
| 7cf19f06ec | |||
| 314ee7ab2d | |||
| 16d67fcd3e | |||
| 508a093e26 | |||
| 97660d4c80 | |||
| 1f4640e4c2 | |||
| b7b9f15063 | |||
| 97432cf9f0 | |||
| c081f80d26 | |||
| 4307d39cc9 | |||
| 61f1b7d3ba | |||
| f4118da3e3 | |||
| dba466a029 | |||
| 019711e24b | |||
| 043110db69 | |||
| b53295ffd2 | |||
| 319ce6fcfd | |||
| 9d76bb3b3a | |||
| 4bc944ebf6 | |||
| d331687392 | |||
| 8a517de1d1 | |||
| bcd3347862 | |||
| aa8e928f0c | |||
| 4529164cdf | |||
| b4140d7772 | |||
| a9809f3581 | |||
| 2cc01756a0 | |||
| a5e9659676 | |||
| 1cf75345ba | |||
| a771600dec | |||
| 15df20fe99 | |||
| 4179178756 | |||
| a3b85c0362 | |||
| 0084d16c98 | |||
| 9ede9ea524 | |||
| e4d2161de4 | |||
| 0a824aac3c | |||
| cbcd5df2f7 | |||
| 31493500f1 | |||
| c3689e0b58 | |||
| d3e455290a | |||
| 66ad67693e | |||
| 43d4097556 | |||
| 1d030b4251 | |||
| d74fdf2f6c | |||
| a4dc1733e8 | |||
| a6fad000e4 | |||
| 0274330a09 | |||
| 119c7e6a3d | |||
| e74ee27256 | |||
| 4288a75d0f | |||
| ada75a04a0 | |||
| f9ec748e91 | |||
| 3b59c79bf4 | |||
| 2cb637b79b | |||
| dff6595f27 | |||
| aac3975e5d | |||
| f69d070dd4 | |||
| d28798ee52 | |||
| 1760d6b8ed | |||
| 80b3436075 | |||
| 8ca0fc4cb0 | |||
| 53c4fd28bf | |||
| 56e8f8b10c | |||
| 1fa6298455 | |||
| e7e0fe4050 | |||
| a7e0d49235 | |||
| 8e85c00452 | |||
| 96a4fe1725 | |||
| 13fe966db3 | |||
| 2da5b4960a | |||
| 00d87b089b | |||
| 9ec30d8168 | |||
| cb21c325f6 | |||
| 35c2d018a5 | |||
| a9f749b4de | |||
| 4cecf7b8e3 | |||
| d661a31041 | |||
| f65b270d2a | |||
| de85a67f28 | |||
| 65e9da693d | |||
| 921333a7bc | |||
| bb9aa2280d | |||
| a38d2fa31e |
+13
-14
@@ -42,16 +42,6 @@ if(NOT APPLE AND NOT WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
|
||||
endif()
|
||||
|
||||
option (ENABLE_OPENMP
|
||||
"Use OpenMP for parallel code" ON)
|
||||
|
||||
if (ENABLE_OPENMP)
|
||||
FIND_PACKAGE( OpenMP )
|
||||
if( OPENMP_FOUND )
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
|
||||
|
||||
add_library(clipper STATIC libs/clipper/clipper.cpp)
|
||||
@@ -64,17 +54,16 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/FffGcodeWriter.cpp
|
||||
src/FffPolygonGenerator.cpp
|
||||
src/FffProcessor.cpp
|
||||
src/FuzzyWalls.cpp
|
||||
src/gcodeExport.cpp
|
||||
src/GCodePathConfig.cpp
|
||||
src/gcodePlanner.cpp
|
||||
src/infill.cpp
|
||||
src/WallsComputation.cpp
|
||||
src/layerPart.cpp
|
||||
src/LayerPlanBuffer.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
src/multiVolumes.cpp
|
||||
src/pathOrderOptimizer.cpp
|
||||
src/Preheat.cpp
|
||||
src/PrimeTower.cpp
|
||||
@@ -82,14 +71,12 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/skin.cpp
|
||||
src/SkirtBrim.cpp
|
||||
src/sliceDataStorage.cpp
|
||||
src/slicer.cpp
|
||||
src/support.cpp
|
||||
src/timeEstimate.cpp
|
||||
src/WallsComputation.cpp
|
||||
src/wallOverlap.cpp
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
src/multithreadOpenMP.cpp
|
||||
|
||||
src/infill/NoZigZagConnectorProcessor.cpp
|
||||
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
|
||||
@@ -98,6 +85,18 @@ set(engine_SRCS # Except main.cpp.
|
||||
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
|
||||
src/infill/SubDivCube.cpp
|
||||
|
||||
src/slicer/LayerPart.cpp
|
||||
src/slicer/MultiVolumes.cpp
|
||||
src/slicer/SlicerLayer.cpp
|
||||
src/slicer/Slicer.cpp
|
||||
|
||||
src/textureProcessing/FaceNormalStorage.cpp
|
||||
src/textureProcessing/Material.cpp
|
||||
src/textureProcessing/MaterialBase.cpp
|
||||
src/textureProcessing/TexturedMesh.cpp
|
||||
src/textureProcessing/TextureBumpMapProcessor.cpp
|
||||
src/textureProcessing/TextureProximityProcessor.cpp
|
||||
|
||||
src/pathPlanning/Comb.cpp
|
||||
src/pathPlanning/GCodePath.cpp
|
||||
src/pathPlanning/LinePolygonsCrossings.cpp
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
https://github.com/nothings/stb
|
||||
|
||||
Thanks to Sean T. Barrett
|
||||
|
||||
license: public domain
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -2,7 +2,7 @@
|
||||
#ifndef CONICAL_OVERHANG_H
|
||||
#define CONICAL_OVERHANG_H
|
||||
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
|
||||
|
||||
namespace cura {
|
||||
|
||||
+82
-88
@@ -6,6 +6,7 @@
|
||||
#include "FffProcessor.h"
|
||||
#include "progress/Progress.h"
|
||||
#include "wallOverlap.h"
|
||||
#include "FuzzyWalls.h"
|
||||
#include "utils/orderOptimizer.h"
|
||||
|
||||
namespace cura
|
||||
@@ -91,15 +92,9 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
|
||||
}
|
||||
}
|
||||
|
||||
#pragma omp parallel default(none) shared(storage, total_layers)
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
#pragma omp single nowait
|
||||
{
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
processLayer(storage, layer_nr, total_layers);
|
||||
}
|
||||
}
|
||||
processLayer(storage, layer_nr, total_layers);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
|
||||
@@ -200,10 +195,7 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_0"), mesh.getSettingInMillimetersPerSecond("jerk_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_x"), mesh.getSettingInMillimetersPerSecond("jerk_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.perimeter_gap_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom"), mesh.getSettingInMicrons("wall_line_width_x") / 2, mesh.getSettingInPercentage("material_flow"));
|
||||
// the perimeter gap config follows the skin config, but has a different line width:
|
||||
// wall_line_width_x divided by two because the gaps are between 0 and 1 times the wall line width
|
||||
|
||||
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMillimetersPerSecond("acceleration_infill"), mesh.getSettingInMillimetersPerSecond("jerk_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
|
||||
@@ -350,10 +342,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
int layer_height = train->getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
@@ -390,10 +379,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
gcode_layer.setExtruder(extruder_nr); // reset to extruder number, because we might have primed in the last layer
|
||||
@@ -425,10 +411,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
|
||||
const int layer_nr = initial_raft_layer_nr + 2 + raftSurfaceLayer - 1; // 2: 1 base layer, 1 interface layer
|
||||
z += layer_height;
|
||||
const int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
gcode_layer.setIsInside(true);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
@@ -512,7 +495,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
|
||||
|
||||
|
||||
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.createPlanner(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, getSettingAsCombingMode("retraction_combing"), comb_offset_from_outlines, avoid_other_parts, avoid_distance);
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, is_inside_mesh_layer_part, fan_speed_layer_time_settings_per_extruder, getSettingAsCombingMode("retraction_combing"), comb_offset_from_outlines, avoid_other_parts, avoid_distance);
|
||||
|
||||
if (include_helper_parts && layer_nr == 0)
|
||||
{ // process the skirt or the brim of the starting extruder.
|
||||
@@ -601,9 +584,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
|
||||
// There should be a synchronization construct to make sure the writegcode task is complete before trimBuffer is called
|
||||
#pragma omp taskwait
|
||||
layer_plan_buffer.trimBuffer();
|
||||
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
@@ -644,7 +625,7 @@ void FffGcodeWriter::processSkirtBrim(SliceDataStorage& storage, GCodePlanner& g
|
||||
{
|
||||
return;
|
||||
}
|
||||
gcode_layer.addTravel(PolygonRef{skirt_brim.back()}.closestPointTo(gcode_layer.getLastPosition()));
|
||||
gcode_layer.addTravel(skirt_brim.back().closestPointTo(gcode_layer.getLastPosition()));
|
||||
gcode_layer.addPolygonsByOptimizer(skirt_brim, &storage.skirt_brim_config[extruder_nr]);
|
||||
}
|
||||
|
||||
@@ -716,7 +697,7 @@ std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& s
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
if (mesh.getSettingAsIndex("extruder_nr") == extruder_nr)
|
||||
{
|
||||
Mesh& mesh_data = storage.meshgroup->meshes[mesh_idx];
|
||||
Mesh& mesh_data = *storage.meshgroup->meshes[mesh_idx];
|
||||
Point3 middle = (mesh_data.getAABB().min + mesh_data.getAABB().max) / 2;
|
||||
mesh_idx_order_optimizer.addItem(Point(middle.x, middle.y), mesh_idx);
|
||||
}
|
||||
@@ -732,7 +713,7 @@ std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& s
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
@@ -748,7 +729,7 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
|
||||
|
||||
const SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
|
||||
Polygons polygons;
|
||||
@@ -757,19 +738,34 @@ void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& stora
|
||||
polygons.add(layer->parts[partNr].outline);
|
||||
}
|
||||
|
||||
PolygonFlowAdjuster* flow_adjuster(nullptr);
|
||||
if (mesh->getSettingBoolean("fuzz_map_enabled") || mesh->getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
FuzzyWalls* fuzzy_processor = new FuzzyWalls(*mesh);
|
||||
flow_adjuster = fuzzy_processor;
|
||||
polygons = fuzzy_processor->makeFuzzy(*mesh, layer_nr, polygons);
|
||||
} else if (mesh->getSettingBoolean("compensate_overlap_0"))
|
||||
{
|
||||
flow_adjuster = new WallOverlapComputation(polygons, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), mesh->getSettingBoolean("magic_spiralize"));
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, flow_adjuster, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), mesh->getSettingBoolean("magic_spiralize"));
|
||||
if (flow_adjuster)
|
||||
{
|
||||
delete flow_adjuster;
|
||||
}
|
||||
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
const SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
Polygons lines;
|
||||
for(ConstPolygonRef polyline : layer->openPolyLines)
|
||||
for(PolygonRef polyline : layer->openPolyLines)
|
||||
{
|
||||
for(unsigned int point_idx = 1; point_idx<polyline.size(); point_idx++)
|
||||
{
|
||||
@@ -783,7 +779,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, cons
|
||||
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
@@ -797,7 +793,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceM
|
||||
return;
|
||||
}
|
||||
|
||||
const SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
if (layer->parts.size() == 0)
|
||||
{
|
||||
@@ -807,7 +803,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceM
|
||||
if (mesh->getSettingAsCount("wall_line_count") > 0)
|
||||
{ // don't switch extruder if there's nothing to print
|
||||
bool empty = true;
|
||||
for (const SliceLayerPart& part : layer->parts)
|
||||
for (SliceLayerPart& part : layer->parts)
|
||||
{
|
||||
if (part.insets.size() > 0)
|
||||
{
|
||||
@@ -839,7 +835,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceM
|
||||
|
||||
for (int part_idx : part_order_optimizer.polyOrder)
|
||||
{
|
||||
const SliceLayerPart& part = layer->parts[part_idx];
|
||||
SliceLayerPart& part = layer->parts[part_idx];
|
||||
addMeshPartToGCode(storage, mesh, part, gcode_layer, layer_nr);
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
@@ -848,7 +844,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, const SliceM
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr)
|
||||
void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
bool skin_alternate_rotation = mesh->getSettingBoolean("skin_alternate_rotation") && ( mesh->getSettingAsCount("top_layers") >= 4 || mesh->getSettingAsCount("bottom_layers") >= 4 );
|
||||
|
||||
@@ -884,9 +880,7 @@ void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, const SliceMe
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
|
||||
}
|
||||
|
||||
EFillMethod skin_pattern = (layer_nr == 0)?
|
||||
mesh->getSettingAsFillMethod("top_bottom_pattern_0") :
|
||||
mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
EFillMethod skin_pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int skin_angle = 45;
|
||||
if ((skin_pattern == EFillMethod::LINES || skin_pattern == EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
@@ -910,7 +904,7 @@ void FffGcodeWriter::addMeshPartToGCode(SliceDataStorage& storage, const SliceMe
|
||||
|
||||
|
||||
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
{
|
||||
int64_t z = layer_nr * getSettingInMicrons("layer_height");
|
||||
if (infill_line_distance > 0)
|
||||
@@ -941,7 +935,7 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, const Sl
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int infill_angle)
|
||||
{
|
||||
if (infill_line_distance == 0 || part.infill_area_per_combine_per_density[0].size() == 0)
|
||||
{
|
||||
@@ -1002,7 +996,7 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, const S
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos)
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos)
|
||||
{
|
||||
bool compensate_overlap_0 = mesh->getSettingBoolean("travel_compensate_overlapping_walls_0_enabled");
|
||||
bool compensate_overlap_x = mesh->getSettingBoolean("travel_compensate_overlapping_walls_x_enabled");
|
||||
@@ -1032,16 +1026,23 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
}
|
||||
if (processed_inset_number == 0)
|
||||
{
|
||||
if (!compensate_overlap_0)
|
||||
Polygons* outer_wall = &part.insets[0];
|
||||
Polygons fuzzy_outer_wall;
|
||||
PolygonFlowAdjuster* flow_adjuster(nullptr);
|
||||
if (mesh->getSettingBoolean("fuzz_map_enabled") || mesh->getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
WallOverlapComputation* wall_overlap_computation(nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
FuzzyWalls* fuzzy_processor = new FuzzyWalls(*mesh);
|
||||
flow_adjuster = fuzzy_processor;
|
||||
fuzzy_outer_wall = fuzzy_processor->makeFuzzy(*mesh, layer_nr, *outer_wall);
|
||||
outer_wall = &fuzzy_outer_wall;
|
||||
} else if (compensate_overlap_0)
|
||||
{
|
||||
flow_adjuster = new WallOverlapComputation(*outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
}
|
||||
else
|
||||
gcode_layer.addPolygonsByOptimizer(*outer_wall, &mesh->inset0_config, flow_adjuster, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
if (flow_adjuster)
|
||||
{
|
||||
Polygons outer_wall = part.insets[0];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
|
||||
delete flow_adjuster;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1052,7 +1053,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons outer_wall = part.insets[processed_inset_number];
|
||||
Polygons& outer_wall = part.insets[processed_inset_number];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_x"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->insetX_config, &wall_overlap_computation);
|
||||
}
|
||||
@@ -1062,35 +1063,31 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, const SliceMeshSto
|
||||
}
|
||||
|
||||
|
||||
void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int skin_angle)
|
||||
void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int skin_angle)
|
||||
{
|
||||
int64_t z = layer_nr * getSettingInMicrons("layer_height");
|
||||
const unsigned int skin_line_width = mesh->skin_config.getLineWidth();
|
||||
const unsigned int perimeter_gaps_line_width = mesh->perimeter_gap_config.getLineWidth();
|
||||
|
||||
constexpr int perimeter_gaps_extra_offset = 15; // extra offset so that the perimeter gaps aren't created everywhere due to rounding errors
|
||||
bool fill_perimeter_gaps = mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE
|
||||
&& !mesh->getSettingBoolean("magic_spiralize");
|
||||
bool fill_perimeter_gaps = mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE;
|
||||
|
||||
Point z_seam_pos(0, 0); // not used
|
||||
PathOrderOptimizer part_order_optimizer(gcode_layer.getLastPosition(), z_seam_pos, EZSeamType::SHORTEST);
|
||||
for (unsigned int skin_part_idx = 0; skin_part_idx < part.skin_parts.size(); skin_part_idx++)
|
||||
{
|
||||
const PolygonsPart& outline = part.skin_parts[skin_part_idx].outline;
|
||||
PolygonsPart& outline = part.skin_parts[skin_part_idx].outline;
|
||||
part_order_optimizer.addPolygon(outline.outerPolygon());
|
||||
}
|
||||
part_order_optimizer.optimize();
|
||||
|
||||
for (int ordered_skin_part_idx : part_order_optimizer.polyOrder)
|
||||
{
|
||||
const SkinPart& skin_part = part.skin_parts[ordered_skin_part_idx];
|
||||
SkinPart& skin_part = part.skin_parts[ordered_skin_part_idx];
|
||||
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
|
||||
EFillMethod pattern = (layer_nr == 0)?
|
||||
mesh->getSettingAsFillMethod("top_bottom_pattern_0") :
|
||||
mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int bridge = -1;
|
||||
if (layer_nr > 0)
|
||||
bridge = bridgeAngle(skin_part.outline, &mesh->layers[layer_nr-1]);
|
||||
@@ -1102,11 +1099,11 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
|
||||
Polygons perimeter_gaps; // the perimeter gaps of the insets of this skin part
|
||||
|
||||
const Polygons* inner_skin_outline = nullptr;
|
||||
Polygons* inner_skin_outline = nullptr;
|
||||
int offset_from_inner_skin_outline = 0;
|
||||
if (pattern != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
for (const Polygons& skin_perimeter : skin_part.insets)
|
||||
for (Polygons& skin_perimeter : skin_part.insets)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->insetX_config); // add polygons to gcode in inward order
|
||||
}
|
||||
@@ -1142,6 +1139,13 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
Infill infill_comp(pattern, *inner_skin_outline, offset_from_inner_skin_outline, skin_line_width, skin_line_width, skin_overlap, skin_angle, z, extra_infill_shift, perimeter_gaps_output);
|
||||
infill_comp.generate(skin_polygons, skin_lines);
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
{ // handle perimeter_gaps of skin insets
|
||||
int offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, skin_line_width, skin_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(skin_polygons, skin_lines);
|
||||
}
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
|
||||
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
@@ -1152,16 +1156,6 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
{ // handle perimeter_gaps of skin insets
|
||||
Polygons gap_polygons; // will remain empty
|
||||
Polygons gap_lines;
|
||||
int offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, perimeter_gaps_line_width, perimeter_gaps_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(gap_polygons, gap_lines);
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh->perimeter_gap_config, SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
if (fill_perimeter_gaps)
|
||||
@@ -1170,6 +1164,10 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
int line_width = mesh->inset0_config.getLineWidth();
|
||||
for (unsigned int inset_idx = 0; inset_idx < part.insets.size() - 1; inset_idx++)
|
||||
{
|
||||
if (inset_idx == 0 && (mesh->getSettingBoolean("fuzz_map_enabled") || mesh->getSettingBoolean("magic_fuzzy_skin_enabled")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Polygons outer = part.insets[inset_idx].offset(-1 * line_width / 2 - perimeter_gaps_extra_offset);
|
||||
line_width = mesh->insetX_config.getLineWidth();
|
||||
|
||||
@@ -1177,31 +1175,27 @@ void FffGcodeWriter::processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, cons
|
||||
perimeter_gaps.add(outer.difference(inner));
|
||||
}
|
||||
{ // gap between inner wall and skin/infill
|
||||
if (mesh->getSettingInMicrons("infill_line_distance") > 0
|
||||
&& !mesh->getSettingBoolean("infill_hollow")
|
||||
&& mesh->getSettingInMicrons("infill_overlap_mm") >= 0
|
||||
)
|
||||
if (mesh->getSettingInMicrons("infill_line_distance") > 0 && !mesh->getSettingBoolean("infill_hollow"))
|
||||
{
|
||||
const Polygons outer = part.insets.back().offset(-1 * line_width / 2 - perimeter_gaps_extra_offset);
|
||||
|
||||
Polygons inner = part.infill_area;
|
||||
for (const SkinPart& skin_part : part.skin_parts)
|
||||
for (SkinPart& skin_part : part.skin_parts)
|
||||
{
|
||||
inner.add(skin_part.outline);
|
||||
}
|
||||
inner = inner.unionPolygons();
|
||||
perimeter_gaps.add(outer.difference(inner));
|
||||
}
|
||||
}
|
||||
|
||||
Polygons gap_polygons; // unused
|
||||
Polygons gap_lines; // soon to be generated gap filler lines
|
||||
Polygons skin_polygons; // unused
|
||||
Polygons skin_lines; // soon to be generated gap filler lines
|
||||
int offset = 0;
|
||||
int extra_infill_shift = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, perimeter_gaps_line_width, perimeter_gaps_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(gap_polygons, gap_lines);
|
||||
Infill infill_comp(EFillMethod::LINES, perimeter_gaps, offset, skin_line_width, skin_line_width, skin_overlap, skin_angle, z, extra_infill_shift);
|
||||
infill_comp.generate(skin_polygons, skin_lines);
|
||||
|
||||
gcode_layer.addLinesByOptimizer(gap_lines, &mesh->perimeter_gap_config, SpaceFillType::Lines);
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1262,7 +1256,7 @@ bool FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
|
||||
PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPosition());
|
||||
for(unsigned int n=0; n<support_islands.size(); n++)
|
||||
{
|
||||
island_order_optimizer.addPolygon(PolygonRef{support_islands[n][0]});
|
||||
island_order_optimizer.addPolygon(support_islands[n][0]);
|
||||
}
|
||||
island_order_optimizer.optimize();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class FffGcodeWriter : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
|
||||
private:
|
||||
int max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
|
||||
coord_t max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
|
||||
|
||||
/*
|
||||
* Buffer for all layer plans (of type GCodePlanner)
|
||||
@@ -301,7 +301,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
|
||||
@@ -312,7 +312,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the layer plan \p gcode_layer.
|
||||
@@ -323,7 +323,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer.
|
||||
@@ -335,7 +335,7 @@ private:
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshPartToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr);
|
||||
void addMeshPartToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
|
||||
@@ -348,7 +348,7 @@ private:
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
*/
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
|
||||
/*!
|
||||
* Add normal sparse infill for a given part in a layer.
|
||||
@@ -360,7 +360,7 @@ private:
|
||||
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
*/
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
|
||||
|
||||
/*!
|
||||
* Generate the insets for the walls of a given layer part.
|
||||
@@ -371,7 +371,7 @@ private:
|
||||
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
|
||||
* \param z_seam_pos The location near where to start the outer inset in case \p z_seam_type is 'back'
|
||||
*/
|
||||
void processInsets(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos);
|
||||
void processInsets(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos);
|
||||
|
||||
|
||||
/*!
|
||||
@@ -387,7 +387,7 @@ private:
|
||||
* \param skin_overlap The distance by which the skin overlaps with the wall insets and the distance by which the perimeter gaps overlap with adjacent print features.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
*/
|
||||
void processSkinAndPerimeterGaps(cura::GCodePlanner& gcode_layer, const cura::SliceMeshStorage* mesh, const cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
|
||||
void processSkinAndPerimeterGaps(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
|
||||
|
||||
/*!
|
||||
* Add the support to the layer plan \p gcodeLayer of the current layer for all support parts with the given \p extruder_nr.
|
||||
|
||||
+46
-171
@@ -2,17 +2,19 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map> // multimap (ordered map allowing duplicate keys)
|
||||
#include <omp.h>
|
||||
#include <functional> // function
|
||||
|
||||
#include "utils/math.h"
|
||||
#include "slicer/Slicer.h"
|
||||
#include "utils/algorithm.h"
|
||||
#include "slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "support.h"
|
||||
#include "multiVolumes.h"
|
||||
#include "layerPart.h"
|
||||
#include "slicer/MultiVolumes.h"
|
||||
#include "slicer/LayerPart.h"
|
||||
#include "textureProcessing/TextureBumpMapProcessor.h"
|
||||
#include "textureProcessing/TextureProximityProcessor.h"
|
||||
#include "WallsComputation.h"
|
||||
#include "SkirtBrim.h"
|
||||
#include "skin.h"
|
||||
@@ -24,7 +26,7 @@
|
||||
#include "progress/ProgressEstimator.h"
|
||||
#include "progress/ProgressStageEstimator.h"
|
||||
#include "progress/ProgressEstimatorLinear.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -32,17 +34,6 @@ namespace cura
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
#pragma omp parallel
|
||||
{
|
||||
#pragma omp master
|
||||
{
|
||||
#ifdef _OPENMP
|
||||
log("OpenMP enabled, number of threads used: %u\n", omp_get_num_threads());
|
||||
#else
|
||||
log("OpenMP disabled\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
@@ -97,11 +88,28 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
|
||||
storage.meshes.reserve(meshgroup->meshes.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
|
||||
for(unsigned int meshIdx=0; meshIdx < meshgroup->meshes.size(); meshIdx++)
|
||||
{
|
||||
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
|
||||
// even make a mesh for a support mesh, which doesn't introduce any parts.
|
||||
storage.meshes.emplace_back(meshgroup->meshes[meshIdx], slice_layer_count); // new mesh in storage had settings from the Mes
|
||||
}
|
||||
// ^ needs to be set already for fuzzy wall texture map processing
|
||||
|
||||
std::vector<Slicer*> slicerList;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = meshgroup->meshes[mesh_idx];
|
||||
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, slice_layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
Mesh& mesh = *meshgroup->meshes[mesh_idx];
|
||||
if (mesh.getSettingBoolean("fuzz_map_enabled"))
|
||||
{
|
||||
TextureProximityProcessor::Settings texture_proximity_processor_settings(mesh.getSettingInMicrons("wall_line_width_0"));
|
||||
storage.meshes[mesh_idx].texture_proximity_processor = new TextureProximityProcessor(texture_proximity_processor_settings, slice_layer_count);
|
||||
}
|
||||
bool keep_open_polylines = mesh.getSettingBoolean("meshfix_keep_open_polygons");
|
||||
bool extensive_stitching = mesh.getSettingBoolean("meshfix_extensive_stitching");
|
||||
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, slice_layer_count, keep_open_polylines, extensive_stitching, storage.meshes[mesh_idx].texture_proximity_processor);
|
||||
slicerList.push_back(slicer);
|
||||
/*
|
||||
for(SlicerLayer& layer : slicer->layers)
|
||||
@@ -119,7 +127,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
|
||||
{
|
||||
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
|
||||
Mesh& mesh = *storage.meshgroup->meshes[meshIdx];
|
||||
if (mesh.getSettingBoolean("conical_overhang_enabled") && !mesh.getSettingBoolean("anti_overhang_mesh"))
|
||||
{
|
||||
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
|
||||
@@ -128,6 +136,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
|
||||
|
||||
if (storage.getSettingBoolean("carve_multiple_volumes"))
|
||||
{
|
||||
carveMultipleVolumes(slicerList, storage.getSettingBoolean("alternate_carve_order"));
|
||||
@@ -137,7 +146,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
storage.print_layer_count = 0;
|
||||
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
|
||||
{
|
||||
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
|
||||
Mesh& mesh = *storage.meshgroup->meshes[meshIdx];
|
||||
Slicer* slicer = slicerList[meshIdx];
|
||||
if (!mesh.getSettingBoolean("anti_overhang_mesh") && !mesh.getSettingBoolean("infill_mesh"))
|
||||
{
|
||||
@@ -146,15 +155,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
}
|
||||
storage.support.supportLayers.resize(storage.print_layer_count);
|
||||
|
||||
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
|
||||
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
|
||||
{
|
||||
Slicer* slicer = slicerList[meshIdx];
|
||||
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
|
||||
Mesh& mesh = *storage.meshgroup->meshes[meshIdx];
|
||||
|
||||
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
|
||||
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
|
||||
SliceMeshStorage& meshStorage = storage.meshes.back();
|
||||
SliceMeshStorage& meshStorage = storage.meshes[meshIdx];
|
||||
|
||||
if (mesh.getSettingBoolean("anti_overhang_mesh"))
|
||||
{
|
||||
@@ -163,10 +169,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
|
||||
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
|
||||
support_layer.anti_overhang = support_layer.anti_overhang.unionPolygons(slicer_layer.polygons);
|
||||
meshStorage.layers[layer_nr].printZ =
|
||||
slicer_layer.z
|
||||
+ getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -177,10 +179,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
|
||||
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
|
||||
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
|
||||
support_layer.support_mesh.add(slicer_layer.polygons);
|
||||
meshStorage.layers[layer_nr].printZ =
|
||||
slicer_layer.z
|
||||
+ getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -357,24 +355,12 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
|
||||
|
||||
|
||||
// walls
|
||||
int processed_layer_count = 0;
|
||||
#pragma omp parallel for default(none) shared(mesh_layer_count, mesh, inset_skin_progress_estimate, processed_layer_count) schedule(dynamic)
|
||||
for(unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
{
|
||||
logDebug("Processing insets for layer %i of %i\n", layer_number, mesh_layer_count);
|
||||
processInsets(mesh, layer_number);
|
||||
#ifdef _OPENMP
|
||||
if (omp_get_thread_num() == 0)
|
||||
#endif
|
||||
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
|
||||
int _processed_layer_count;
|
||||
#pragma omp atomic read
|
||||
_processed_layer_count = processed_layer_count;
|
||||
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
|
||||
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
|
||||
}
|
||||
#pragma omp atomic
|
||||
processed_layer_count++;
|
||||
double progress = inset_skin_progress_estimate.progress(layer_number);
|
||||
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
|
||||
}
|
||||
|
||||
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count);
|
||||
@@ -389,8 +375,8 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
|
||||
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
|
||||
if (other_mesh.getSettingBoolean("infill_mesh"))
|
||||
{
|
||||
AABB3D aabb = storage.meshgroup->meshes[mesh_idx].getAABB();
|
||||
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx].getAABB();
|
||||
AABB3D aabb = storage.meshgroup->meshes[mesh_idx]->getAABB();
|
||||
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx]->getAABB();
|
||||
if (aabb.hit(other_aabb))
|
||||
{
|
||||
process_infill = true;
|
||||
@@ -405,32 +391,15 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
|
||||
{
|
||||
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
|
||||
}
|
||||
|
||||
processed_layer_count = 0;
|
||||
#pragma omp parallel default(none) shared(mesh_layer_count, mesh, mesh_max_bottom_layer_count, process_infill, inset_skin_progress_estimate, processed_layer_count)
|
||||
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
{
|
||||
|
||||
#pragma omp for schedule(dynamic)
|
||||
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
|
||||
logDebug("Processing skins and infill layer %i of %i\n", layer_number, mesh_layer_count);
|
||||
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
|
||||
{
|
||||
logDebug("Processing skins and infill layer %i of %i\n", layer_number, mesh_layer_count);
|
||||
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
|
||||
{
|
||||
processSkinsAndInfill(mesh, layer_number, process_infill);
|
||||
}
|
||||
#ifdef _OPENMP
|
||||
if (omp_get_thread_num() == 0)
|
||||
#endif
|
||||
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
|
||||
int _processed_layer_count;
|
||||
#pragma omp atomic read
|
||||
_processed_layer_count = processed_layer_count;
|
||||
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
|
||||
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
|
||||
}
|
||||
#pragma omp atomic
|
||||
processed_layer_count++;
|
||||
processSkinsAndInfill(mesh, layer_number, process_infill);
|
||||
}
|
||||
double progress = inset_skin_progress_estimate.progress(layer_number);
|
||||
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +439,7 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
|
||||
if (new_outline.size() == 1)
|
||||
{ // we don't have to call splitIntoParts, because a single polygon can only be a single part
|
||||
PolygonsPart outline_part_here;
|
||||
outline_part_here.add(PolygonRef{new_outline[0]});
|
||||
outline_part_here.add(new_outline[0]);
|
||||
new_parts.push_back(outline_part_here);
|
||||
}
|
||||
else if (new_outline.size() > 1)
|
||||
@@ -519,22 +488,8 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
|
||||
// combine infill
|
||||
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1))); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
|
||||
// fuzzy skin
|
||||
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
processFuzzyWalls(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateInsets only reads and writes data for the current layer
|
||||
*
|
||||
* processInsets only reads and writes data for the current layer
|
||||
*/
|
||||
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
|
||||
{
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
@@ -573,19 +528,11 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, cons
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_idx];
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0)
|
||||
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
|
||||
{
|
||||
layer_is_empty = false;
|
||||
break;
|
||||
}
|
||||
for (const SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
if (part.print_outline.size() > 0)
|
||||
{
|
||||
layer_is_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer_is_empty)
|
||||
@@ -616,18 +563,8 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, cons
|
||||
support_layers.erase(support_layers.begin(), support_layers.begin() + n_empty_first_layers);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateSkins read (depend on) data from mesh.layers[*].parts[*].insets and write mesh.layers[n].parts[*].skin_parts
|
||||
* generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area
|
||||
*
|
||||
* processSkinsAndInfill read (depend on) mesh.layers[*].parts[*].{insets,boundingBox}.
|
||||
* write mesh.layers[n].parts[*].{skin_parts,infill_area}.
|
||||
*/
|
||||
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
|
||||
|
||||
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
|
||||
{
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
@@ -781,66 +718,4 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
}
|
||||
|
||||
|
||||
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
{
|
||||
if (mesh.getSettingAsCount("wall_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
|
||||
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons results;
|
||||
Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0];
|
||||
for (PolygonRef poly : skin)
|
||||
{
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
for (Point& p1 : poly)
|
||||
{ // 'a' is the (next) new point between p0 and p1
|
||||
Point p0p1 = p1 - *p0;
|
||||
int64_t p0p1_size = vSize(p0p1);
|
||||
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
int r = rand() % (fuzziness * 2) - fuzziness;
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
|
||||
p0 = &p1;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
if (point_idx == 0) { break; }
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
for (Point& p : poly)
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -38,7 +38,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
|
||||
}
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = meshgroup.meshes[mesh_idx];
|
||||
Mesh& mesh = *meshgroup.meshes[mesh_idx];
|
||||
sstream << " -e" << mesh.getSettingAsIndex("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
|
||||
}
|
||||
sstream << "\n";
|
||||
@@ -58,9 +58,9 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
gcode_writer.setParent(meshgroup);
|
||||
|
||||
bool empty = true;
|
||||
for (Mesh& mesh : meshgroup->meshes)
|
||||
for (Mesh* mesh : meshgroup->meshes)
|
||||
{
|
||||
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
|
||||
if (!mesh->getSettingBoolean("infill_mesh") && !mesh->getSettingBoolean("anti_overhang_mesh"))
|
||||
{
|
||||
empty = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#include "FuzzyWalls.h"
|
||||
|
||||
#define NORMAL_LENGTH 10000
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
FuzzyWalls::FuzzyWalls(const SliceMeshStorage& mesh)
|
||||
: settings(&mesh)
|
||||
{
|
||||
if (mesh.getSettingBoolean("fuzz_map_enabled"))
|
||||
{
|
||||
assert(mesh.texture_proximity_processor && "texture_proximity_processor should have been initialized");
|
||||
getAmplitude = [&mesh, this](const unsigned int layer_nr, const Point p)
|
||||
{
|
||||
assert(mesh.texture_proximity_processor && "When fuzz_map_enabled there has to be a texture proximity processor!");
|
||||
TextureProximityProcessor& texture_proximity_processor = *mesh.texture_proximity_processor;
|
||||
float color = texture_proximity_processor.getColor(p, layer_nr, settings.color_usage, 0.0); // TODO change default 0.0
|
||||
coord_t ret = color * settings.max_amplitude;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
getAmplitude = [this](const unsigned int layer_nr, const Point p)
|
||||
{
|
||||
return settings.max_amplitude;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Polygons FuzzyWalls::makeFuzzy(const SliceMeshStorage& mesh, const unsigned int layer_nr, const Polygons& in)
|
||||
{
|
||||
Polygons results;
|
||||
if (in.size() == 0)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
flows.reserve(in.size());
|
||||
for (const PolygonRef poly : const_cast<Polygons&>(in))
|
||||
{
|
||||
assert(poly.size() >= 3);
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
flows.emplace_back(); // keep flows aligned with the result
|
||||
flows.back().reserve(poly.size());
|
||||
|
||||
Point p0 = poly[poly.size() - 2];
|
||||
Point p1 = poly.back();
|
||||
for (int p0_idx = poly.size() - 2; p0_idx >= 0; p0_idx--)
|
||||
{ // p0 is the last point before p1 which is different from p1
|
||||
p0 = poly[p0_idx];
|
||||
}
|
||||
CarryOver carry_over;
|
||||
carry_over.dist_left_over = (settings.min_dist_between_points + rand() % settings.range_random_point_dist) / 2;
|
||||
carry_over.step_size = carry_over.dist_left_over;
|
||||
carry_over.offset_random = 0.0; // unused in the first iteration since carry_over.step_size = carry_over.dist_left_over; see makeCornerFuzzy
|
||||
carry_over.next_offset_random = static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 2.0 - 1.0;
|
||||
carry_over.p0p1_perp = turn90CCW(p1 - p0);
|
||||
// 'x' is the previous location from where a randomly offsetted new point between p-1 and p0 was created
|
||||
for (Point p2 : poly)
|
||||
{
|
||||
if (p2 == p1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
makeCornerFuzzy(layer_nr, p0, p1, p2, carry_over, result);
|
||||
makeSegmentFuzzy(layer_nr, p1, p2, result, carry_over);
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
flows.back().push_back(1.0);
|
||||
if (point_idx == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() > 0)
|
||||
{ // compute flow of the newly introduced segment
|
||||
const Point p0 = result.back();
|
||||
const Point p1 = result.back();
|
||||
const coord_t length = vSize(p1 - p0);
|
||||
const coord_t pxpa_dist = carry_over.step_size - carry_over.dist_left_over;
|
||||
const float flow_here = (length < 10 || std::abs(length - pxpa_dist) < 5)? 1.0 : std::min(1.0, INT2MM(pxpa_dist) / INT2MM(length));
|
||||
flows.back().push_back(flow_here);
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
flows.back().clear();
|
||||
for (const Point& p : poly)
|
||||
{
|
||||
result.add(p);
|
||||
flows.back().push_back(1.0);
|
||||
}
|
||||
}
|
||||
assert(result.size() == flows.back().size());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void FuzzyWalls::makeCornerFuzzy(const unsigned int layer_nr, const Point p0, const Point p1, const Point p2, const CarryOver carry_over, PolygonRef result)
|
||||
{
|
||||
const Point p0p1_perp = carry_over.p0p1_perp;
|
||||
const Point p1p2 = p2 - p1;
|
||||
const Point p1p2_perp = turn90CCW(p1p2);
|
||||
const Point corner_normal = normal(p0p1_perp, NORMAL_LENGTH) + normal(p1p2_perp, NORMAL_LENGTH);
|
||||
|
||||
// x is the last point which was offsetted
|
||||
// a is the next point to be offsetted
|
||||
//
|
||||
// step_size
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// p1pa_dist
|
||||
// pxp1_dist ^^^^^^^^^
|
||||
// ^^^^^^^^^^
|
||||
// ┬ > amplitudes
|
||||
// |
|
||||
// |
|
||||
// ┬ |
|
||||
// ┥ | > previous random offset within amplitude
|
||||
// | ┥pr ┬ > corner offset computed by weighted average based on pxp0_dist, p0pa_dist and the amplitudes
|
||||
// | | |
|
||||
// -------x---------p1--------a-------
|
||||
// | | ┥ > next random offset within amplitude
|
||||
// | | ┴
|
||||
// | |
|
||||
// ┴ |
|
||||
// |
|
||||
// |
|
||||
// ┴
|
||||
//
|
||||
// assuming all amplitudes are the same and x, p1, a are on a straight line, pr will also be on a straight line between the previous and next offsetted points
|
||||
|
||||
const coord_t corner_amplitude = getAmplitude(layer_nr, p1);
|
||||
// randFloat = offset / amplitude
|
||||
// offset weighted by relative amplitudes and distance to p0
|
||||
assert(carry_over.step_size > 0);
|
||||
const coord_t pxp1_dist = (carry_over.step_size - carry_over.dist_left_over);
|
||||
assert(pxp1_dist >= 0);
|
||||
const coord_t p1pa_dist = carry_over.dist_left_over;
|
||||
const coord_t offset_contribution_0 = corner_amplitude * pxp1_dist * carry_over.offset_random;
|
||||
const coord_t offset_contribution_2 = corner_amplitude * p1pa_dist * carry_over.next_offset_random;
|
||||
const coord_t offset = (offset_contribution_0 + offset_contribution_2) / carry_over.step_size;
|
||||
|
||||
Point fuzz = normal(corner_normal, offset);
|
||||
Point pr = p1 + fuzz;
|
||||
if (result.size() > 0)
|
||||
{ // compute flow of the newly introduced segment
|
||||
const Point last = result.back();
|
||||
const coord_t length = vSize(last - pr);
|
||||
const float flow_here = (length < 10 || std::abs(length - pxp1_dist) < 5)? 1.0 : std::min(1.0, INT2MM(pxp1_dist) / INT2MM(length));
|
||||
// limit the flow to 1.0,
|
||||
// internal corners where the offset is negative could result in such a case,
|
||||
// but it is then better to not cause over extrusion there
|
||||
flows.back().push_back(flow_here);
|
||||
}
|
||||
result.add(pr);
|
||||
}
|
||||
|
||||
void FuzzyWalls::makeSegmentFuzzy(const unsigned int layer_nr, const Point p0, const Point p1, PolygonRef result, CarryOver& carry_over)
|
||||
{
|
||||
// 'a' is the (next) new point between p0 and p1, offsetted from the point
|
||||
// 'x', which is on the line segment p0p1
|
||||
const Point p0p1 = p1 - p0;
|
||||
carry_over.p0p1_perp = turn90CCW(p0p1);
|
||||
const int64_t p0p1_size = vSize(p0p1);
|
||||
coord_t dist_to_prev_point = carry_over.dist_left_over; // distance from the last introduced point to the newly introduced one
|
||||
int64_t dist_last_point = carry_over.dist_left_over - carry_over.step_size; // so that 'carry_over.step_size - (p0p1_size - dist_last_point)' evaulates to 'dist_left_over - p0p1_size'
|
||||
for (int64_t p0pa_dist = carry_over.dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += carry_over.step_size)
|
||||
{
|
||||
const Point px = p0 + normal(p0p1, p0pa_dist);
|
||||
coord_t amplitude = getAmplitude(layer_nr, px);
|
||||
if (amplitude == 0)
|
||||
{
|
||||
amplitude = 1;
|
||||
}
|
||||
carry_over.offset_random = carry_over.next_offset_random;
|
||||
carry_over.next_offset_random = static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 2.0 - 1.0;
|
||||
const coord_t offset = carry_over.offset_random * amplitude;
|
||||
Point fuzz = normal(carry_over.p0p1_perp, offset);
|
||||
Point pa = px + fuzz;
|
||||
if (result.size() > 0)
|
||||
{ // compute flow of the newly introduced segment
|
||||
const Point last = result.back();
|
||||
const coord_t length = vSize(last - pa);
|
||||
const float flow_here = (length < 10 || std::abs(length - dist_to_prev_point) < 5)? 1.0 : std::min(1.0, INT2MM(dist_to_prev_point) / INT2MM(length));
|
||||
flows.back().push_back(flow_here);
|
||||
}
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
carry_over.step_size = settings.min_dist_between_points + rand() % settings.range_random_point_dist;
|
||||
dist_to_prev_point = carry_over.step_size;
|
||||
}
|
||||
carry_over.dist_left_over = carry_over.step_size - (p0p1_size - dist_last_point);
|
||||
assert(carry_over.dist_left_over >= 0);
|
||||
assert(carry_over.dist_left_over < carry_over.step_size);
|
||||
}
|
||||
|
||||
float FuzzyWalls::getFlow(const Polygons& from, unsigned int poly_idx, unsigned int from_point_idx, unsigned int to_point_idx)
|
||||
{
|
||||
assert(from.size() == flows.size());
|
||||
assert(poly_idx < flows.size());
|
||||
assert(from[poly_idx].size() == flows[poly_idx].size());
|
||||
assert((from_point_idx + 1) % flows[poly_idx].size() == to_point_idx);
|
||||
return flows[poly_idx][from_point_idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,52 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef FUZZY_WALLS_H
|
||||
#define FUZZY_WALLS_H
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
#include "PolygonFlowAdjuster.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
class FuzzyWalls : public PolygonFlowAdjuster
|
||||
{
|
||||
public:
|
||||
struct Settings
|
||||
{
|
||||
coord_t max_amplitude;
|
||||
coord_t avg_dist_between_points;
|
||||
ColourUsage color_usage;
|
||||
coord_t min_dist_between_points;
|
||||
coord_t range_random_point_dist;
|
||||
Settings(const SettingsBaseVirtual* settings_base)
|
||||
: max_amplitude(settings_base->getSettingInMicrons("magic_fuzzy_skin_thickness"))
|
||||
, avg_dist_between_points(settings_base->getSettingInMicrons("magic_fuzzy_skin_point_dist"))
|
||||
, color_usage(settings_base->getSettingAsColourUsage("fuzz_map_texture_color"))
|
||||
, min_dist_between_points(avg_dist_between_points * 3 / 4) // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
, range_random_point_dist(avg_dist_between_points / 2)
|
||||
{
|
||||
}
|
||||
};
|
||||
FuzzyWalls(const SliceMeshStorage& mesh);
|
||||
Polygons makeFuzzy(const SliceMeshStorage& mesh, const unsigned int layer_nr, const Polygons& in);
|
||||
float getFlow(const Polygons& from, unsigned int poly_idx, unsigned int from_point_idx, unsigned int to_point_idx);
|
||||
protected:
|
||||
struct CarryOver
|
||||
{
|
||||
coord_t dist_left_over;
|
||||
float offset_random; // [-1,1]
|
||||
float next_offset_random; // [-1,1]
|
||||
coord_t step_size;
|
||||
Point p0p1_perp;
|
||||
};
|
||||
Settings settings;
|
||||
std::function<coord_t (const unsigned int, const Point)> getAmplitude;
|
||||
|
||||
std::vector<std::vector<float>> flows; //!< The flow per segment per polygon in the input
|
||||
|
||||
void makeCornerFuzzy(const unsigned int layer_nr, const Point p0, const Point p1, const Point p2, const CarryOver carry_over, PolygonRef result);
|
||||
void makeSegmentFuzzy(const unsigned int layer_nr, const Point p0, const Point p1, PolygonRef result, CarryOver& carry_over);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//FUZZY_WALLS_H
|
||||
@@ -67,37 +67,37 @@ void GCodePathConfig::setSpeedIconic()
|
||||
current_config.jerk = iconic_config.jerk;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getExtrusionMM3perMM() const
|
||||
double GCodePathConfig::getExtrusionMM3perMM()
|
||||
{
|
||||
return extrusion_mm3_per_mm;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getSpeed() const
|
||||
double GCodePathConfig::getSpeed()
|
||||
{
|
||||
return current_config.speed;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getAcceleration() const
|
||||
double GCodePathConfig::getAcceleration()
|
||||
{
|
||||
return current_config.acceleration;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getJerk() const
|
||||
double GCodePathConfig::getJerk()
|
||||
{
|
||||
return current_config.jerk;
|
||||
}
|
||||
|
||||
int GCodePathConfig::getLineWidth() const
|
||||
int GCodePathConfig::getLineWidth()
|
||||
{
|
||||
return current_config.line_width;
|
||||
}
|
||||
|
||||
bool GCodePathConfig::isTravelPath() const
|
||||
bool GCodePathConfig::isTravelPath()
|
||||
{
|
||||
return current_config.line_width == 0;
|
||||
}
|
||||
|
||||
double GCodePathConfig::getFlowPercentage() const
|
||||
double GCodePathConfig::getFlowPercentage()
|
||||
{
|
||||
return current_config.flow;
|
||||
}
|
||||
|
||||
@@ -79,28 +79,28 @@ public:
|
||||
/*!
|
||||
* Can only be called after the layer height has been set (which is done while writing the gcode!)
|
||||
*/
|
||||
double getExtrusionMM3perMM() const;
|
||||
double getExtrusionMM3perMM();
|
||||
|
||||
/*!
|
||||
* Get the movement speed in mm/s
|
||||
*/
|
||||
double getSpeed() const;
|
||||
double getSpeed();
|
||||
|
||||
/*!
|
||||
* Get the current acceleration of this config
|
||||
*/
|
||||
double getAcceleration() const;
|
||||
double getAcceleration();
|
||||
|
||||
/*!
|
||||
* Get the current jerk of this config
|
||||
*/
|
||||
double getJerk() const;
|
||||
double getJerk();
|
||||
|
||||
int getLineWidth() const;
|
||||
int getLineWidth();
|
||||
|
||||
bool isTravelPath() const;
|
||||
bool isTravelPath();
|
||||
|
||||
double getFlowPercentage() const;
|
||||
double getFlowPercentage();
|
||||
|
||||
private:
|
||||
void calculateExtrusion();
|
||||
|
||||
@@ -7,31 +7,6 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
void issueWriteGCode_impl(
|
||||
GCodeExport* p_gcode,
|
||||
GCodePlanner* p_front_buffer
|
||||
){
|
||||
#pragma omp task default(none) firstprivate(p_gcode, p_front_buffer)
|
||||
{ MULTITHREAD_TASK_CATCH_EXCEPTION(
|
||||
GCodeExport& gcode_ref = *p_gcode;
|
||||
#ifdef _OPENMP
|
||||
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode_ref.getOutputStreamLock());
|
||||
#endif
|
||||
p_front_buffer->writeGCode(gcode_ref);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
)}
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::issueWriteGCode()
|
||||
{
|
||||
assert(!(buffer.front().isGCodeWritten()) && "GCode shouldn't be written more than once");
|
||||
GCodeExport* p_gcode = &gcode;
|
||||
GCodePlanner* p_front_buffer = &buffer.front();
|
||||
issueWriteGCode_impl(p_gcode, p_front_buffer);
|
||||
}
|
||||
|
||||
|
||||
void LayerPlanBuffer::flush()
|
||||
@@ -40,10 +15,6 @@ void LayerPlanBuffer::flush()
|
||||
{
|
||||
insertTempCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode.getOutputStreamLock());
|
||||
#endif
|
||||
while (!buffer.empty())
|
||||
{
|
||||
buffer.front().writeGCode(gcode);
|
||||
@@ -59,10 +30,9 @@ void LayerPlanBuffer::flush()
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
{
|
||||
double acc_time = 0.0;
|
||||
std::vector<GCodePath>& extruder_plan_before_paths = extruder_plan_before.getPaths();
|
||||
for (unsigned int path_idx = extruder_plan_before_paths.size() - 1; int(path_idx) != -1 ; path_idx--)
|
||||
for (unsigned int path_idx = extruder_plan_before.paths.size() - 1; int(path_idx) != -1 ; path_idx--)
|
||||
{
|
||||
GCodePath& path = extruder_plan_before_paths[path_idx];
|
||||
GCodePath& path = extruder_plan_before.paths[path_idx];
|
||||
const double time_this_path = path.estimates.getTotalTime();
|
||||
acc_time += time_this_path;
|
||||
if (acc_time > time_after_extruder_plan_start)
|
||||
@@ -225,9 +195,9 @@ void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan)
|
||||
if (preheat_config.getInitialPrintTemp(extruder) != 0)
|
||||
{ // handle heating from initial_print_temperature to printing_tempreature
|
||||
unsigned int path_idx;
|
||||
for (path_idx = 0; path_idx < extruder_plan.getPaths().size(); path_idx++)
|
||||
for (path_idx = 0; path_idx < extruder_plan.paths.size(); path_idx++)
|
||||
{
|
||||
GCodePath& path = extruder_plan.getPaths()[path_idx];
|
||||
GCodePath& path = extruder_plan.paths[path_idx];
|
||||
heated_pre_travel_time += path.estimates.getTotalTime();
|
||||
if (!path.isTravelPath())
|
||||
{
|
||||
@@ -254,9 +224,9 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
double heated_post_travel_time = 0; // The time after the last extrude move toward the end of the extruder plan during which the nozzle is stable at the final print temperature
|
||||
{ // compute heated_post_travel_time
|
||||
unsigned int path_idx;
|
||||
for (path_idx = last_extruder_plan.getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
for (path_idx = last_extruder_plan.paths.size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
{
|
||||
GCodePath& path = last_extruder_plan.getPaths()[path_idx];
|
||||
GCodePath& path = last_extruder_plan.paths[path_idx];
|
||||
if (!path.isTravelPath())
|
||||
{
|
||||
break;
|
||||
@@ -336,9 +306,9 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
{ // insert temp command in precool_extruder_plan
|
||||
double extrusion_time_seen = 0;
|
||||
unsigned int path_idx;
|
||||
for (path_idx = precool_extruder_plan->getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
for (path_idx = precool_extruder_plan->paths.size() - 1; int(path_idx) >= 0; path_idx--)
|
||||
{
|
||||
GCodePath& path = precool_extruder_plan->getPaths()[path_idx];
|
||||
GCodePath& path = precool_extruder_plan->paths[path_idx];
|
||||
extrusion_time_seen += path.estimates.getTotalTime();
|
||||
if (extrusion_time_seen >= cool_down_time)
|
||||
{
|
||||
@@ -354,16 +324,11 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
|
||||
|
||||
void LayerPlanBuffer::insertTempCommands()
|
||||
{
|
||||
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].getPathsList().empty()))
|
||||
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
|
||||
{ // disregard empty layer
|
||||
buffer.pop_back();
|
||||
return;
|
||||
}
|
||||
for (ExtruderPlan& plan: buffer.back().extruder_plans)
|
||||
{
|
||||
plan.convertListToVector();
|
||||
}
|
||||
|
||||
|
||||
std::vector<ExtruderPlan*> extruder_plans;
|
||||
extruder_plans.reserve(buffer.size() * 2);
|
||||
|
||||
+8
-20
@@ -57,38 +57,26 @@ public:
|
||||
|
||||
/*!
|
||||
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
|
||||
* Pop back the oldest layer plan is it exceeds the buffer size and write it to gcode.
|
||||
*/
|
||||
template<typename... Args>
|
||||
GCodePlanner& createPlanner(Args&&... constructor_args)
|
||||
GCodePlanner& emplace_back(Args&&... constructor_args)
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertTempCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
|
||||
}
|
||||
buffer.emplace_back(constructor_args...);
|
||||
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
issueWriteGCode();
|
||||
}
|
||||
return buffer.back();
|
||||
}
|
||||
|
||||
/*
|
||||
* Write GCode for the oldest layer plan.
|
||||
*/
|
||||
void issueWriteGCode();
|
||||
|
||||
/*
|
||||
* Pop back the oldest layer plan if it exceeds the buffer size and it has been written to gcode.
|
||||
*/
|
||||
void trimBuffer()
|
||||
{
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
assert(buffer.front().isGCodeWritten() && "GCode should be written before planner is discarded");
|
||||
buffer.front().writeGCode(gcode);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
}
|
||||
buffer.pop_front();
|
||||
}
|
||||
return buffer.back();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
+170
-22
@@ -25,6 +25,10 @@ void* fgets_(char* ptr, size_t len, FILE* f)
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
else if (*ptr =='\0')
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
ptr++;
|
||||
len--;
|
||||
}
|
||||
@@ -45,6 +49,10 @@ MeshGroup::~MeshGroup()
|
||||
delete extruders[extruder];
|
||||
}
|
||||
}
|
||||
for (Mesh* mesh : meshes)
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
|
||||
int MeshGroup::getExtruderCount() const
|
||||
@@ -90,10 +98,10 @@ Point3 MeshGroup::min() const
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0].min();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
Point3 ret = meshes[0]->min();
|
||||
for (unsigned int i = 1; i < meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i].min();
|
||||
Point3 v = meshes[i]->min();
|
||||
ret.x = std::min(ret.x, v.x);
|
||||
ret.y = std::min(ret.y, v.y);
|
||||
ret.z = std::min(ret.z, v.z);
|
||||
@@ -107,10 +115,10 @@ Point3 MeshGroup::max() const
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0].max();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
Point3 ret = meshes[0]->max();
|
||||
for (unsigned int i = 1; i < meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i].max();
|
||||
Point3 v = meshes[i]->max();
|
||||
ret.x = std::max(ret.x, v.x);
|
||||
ret.y = std::max(ret.y, v.y);
|
||||
ret.z = std::max(ret.z, v.z);
|
||||
@@ -120,9 +128,9 @@ Point3 MeshGroup::max() const
|
||||
|
||||
void MeshGroup::clear()
|
||||
{
|
||||
for(Mesh& m : meshes)
|
||||
for (Mesh* m : meshes)
|
||||
{
|
||||
m.clear();
|
||||
m->clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,9 +148,9 @@ void MeshGroup::finalize()
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const Mesh& mesh : meshes)
|
||||
for (const Mesh* mesh : meshes)
|
||||
{
|
||||
if (mesh.getSettingBoolean("support_enable")
|
||||
if (mesh->getSettingBoolean("support_enable")
|
||||
&& (
|
||||
getSettingAsIndex("support_infill_extruder_nr") == extruder_nr
|
||||
|| getSettingAsIndex("support_extruder_nr_layer_0") == extruder_nr
|
||||
@@ -156,13 +164,13 @@ void MeshGroup::finalize()
|
||||
}
|
||||
}
|
||||
|
||||
for (const Mesh& mesh : meshes)
|
||||
for (const Mesh* mesh : meshes)
|
||||
{
|
||||
if (!mesh.getSettingBoolean("anti_overhang_mesh")
|
||||
&& !mesh.getSettingBoolean("support_mesh")
|
||||
if (!mesh->getSettingBoolean("anti_overhang_mesh")
|
||||
&& !mesh->getSettingBoolean("support_mesh")
|
||||
)
|
||||
{
|
||||
getExtruderTrain(mesh.getSettingAsIndex("extruder_nr"))->setIsUsed(true);
|
||||
getExtruderTrain(mesh->getSettingAsIndex("extruder_nr"))->setIsUsed(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,17 +183,17 @@ void MeshGroup::finalize()
|
||||
}
|
||||
|
||||
// If a mesh position was given, put the mesh at this position in 3D space.
|
||||
for(Mesh& mesh : meshes)
|
||||
for (Mesh* mesh : meshes)
|
||||
{
|
||||
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
|
||||
if (mesh.getSettingBoolean("center_object"))
|
||||
Point3 mesh_offset(mesh->getSettingInMicrons("mesh_position_x"), mesh->getSettingInMicrons("mesh_position_y"), mesh->getSettingInMicrons("mesh_position_z"));
|
||||
if (mesh->getSettingBoolean("center_object"))
|
||||
{
|
||||
Point3 object_min = mesh.min();
|
||||
Point3 object_max = mesh.max();
|
||||
Point3 object_min = mesh->min();
|
||||
Point3 object_max = mesh->max();
|
||||
Point3 object_size = object_max - object_min;
|
||||
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
|
||||
}
|
||||
mesh.offset(mesh_offset + meshgroup_offset);
|
||||
mesh->offset(mesh_offset + meshgroup_offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +337,128 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
return loadMeshSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
|
||||
void loadMaterialBase(TexturedMesh* mesh, const char* filename)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
if (f == nullptr)
|
||||
{
|
||||
logError("ERROR: Couldn't load MTL file %s.\n", filename);
|
||||
return;
|
||||
}
|
||||
char buffer[1024];
|
||||
char mat_name [100];
|
||||
char mat_file [100];
|
||||
char map_type [10];
|
||||
Material* last_mat = nullptr;
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (buffer[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sscanf(buffer, "map_%s %s", map_type, mat_file) == 2 // we don't care what type of map it specifies (currently)
|
||||
|| sscanf(buffer, "bump %s", mat_file) == 1
|
||||
|| sscanf(buffer, "disp %s", mat_file) == 1
|
||||
|| sscanf(buffer, "decal %s", mat_file) == 1
|
||||
|| sscanf(buffer, "refl %s", mat_file) == 1
|
||||
)
|
||||
{
|
||||
std::string parent_dir = std::string(filename).substr(0, std::string(filename).find_last_of("/\\"));
|
||||
std::string mtl_file = parent_dir + "/" + mat_file;
|
||||
if (last_mat)
|
||||
{
|
||||
last_mat->loadImage(mtl_file.c_str());
|
||||
}
|
||||
}
|
||||
else if (sscanf(buffer, "newmtl %s", mat_name) == 1)
|
||||
{
|
||||
last_mat = mesh->addMaterial(mat_name);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
bool loadMeshOBJ(TexturedMesh* mesh, const char* filename, const FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
if (f == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
char buffer[1024];
|
||||
FPoint3 vertex;
|
||||
int vertex_indices[3];
|
||||
float texture_x;
|
||||
float texture_y;
|
||||
float temp;
|
||||
char face_index_buffer_1 [100];
|
||||
char face_index_buffer_2 [100];
|
||||
char face_index_buffer_3 [100];
|
||||
char str_buffer [100];
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (buffer[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (sscanf(buffer, "v %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
{
|
||||
Point3 v = matrix.apply(vertex);
|
||||
mesh->addVertex(v);
|
||||
}
|
||||
else if (sscanf(buffer, "vt %f %f", &texture_x, &texture_y) == 2)
|
||||
{
|
||||
mesh->addTextureCoord(texture_x, texture_y);
|
||||
}
|
||||
else if (sscanf(buffer, "f %s %s %s", face_index_buffer_1, face_index_buffer_2, face_index_buffer_3) == 3)
|
||||
{
|
||||
int normal_vector_index; // unused
|
||||
int texture_indices[3]; // becomes -1 if no texture data supplied
|
||||
int n_scanned_1 = sscanf(face_index_buffer_1, "%d/%d/%d", &vertex_indices[0], &texture_indices[0], &normal_vector_index);
|
||||
int n_scanned_2 = sscanf(face_index_buffer_2, "%d/%d/%d", &vertex_indices[1], &texture_indices[1], &normal_vector_index);
|
||||
int n_scanned_3 = sscanf(face_index_buffer_3, "%d/%d/%d", &vertex_indices[2], &texture_indices[2], &normal_vector_index);
|
||||
if (n_scanned_1 >= 2 && n_scanned_2 >= 2 && n_scanned_3 >= 2)
|
||||
{
|
||||
mesh->addFace(vertex_indices[0] - 1, vertex_indices[1] - 1, vertex_indices[2] - 1, texture_indices[0] - 1, texture_indices[1] - 1, texture_indices[2] - 1);
|
||||
// obj files count vertex indices starting from 1!
|
||||
}
|
||||
else if (n_scanned_1 >= 1 && n_scanned_2 >= 1 && n_scanned_3 >= 1)
|
||||
{
|
||||
mesh->addFace(vertex_indices[0] - 1, vertex_indices[1] - 1, vertex_indices[2] - 1);
|
||||
}
|
||||
}
|
||||
else if (sscanf(buffer, "mtllib %s", str_buffer) == 1)
|
||||
{
|
||||
std::string parent_dir = std::string(filename).substr(0, std::string(filename).find_last_of("/\\"));
|
||||
std::string mtl_file = parent_dir + "/" + str_buffer;
|
||||
loadMaterialBase(mesh, mtl_file.c_str());
|
||||
}
|
||||
else if (sscanf(buffer, "usemtl %s", str_buffer) == 1)
|
||||
{
|
||||
mesh->setMaterial(str_buffer);
|
||||
}
|
||||
else if (sscanf(buffer, "vn %f %f %f", &temp, &temp, &temp) == 3)
|
||||
{
|
||||
// do nothing with vertex normals
|
||||
}
|
||||
else if (sscanf(buffer, "g %s", str_buffer) == 1)
|
||||
{
|
||||
// do nothing with polygon groups
|
||||
}
|
||||
else if (buffer[0] == '\0')
|
||||
{
|
||||
// empty line, do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("Cannot parse line \"%s\"\n", buffer);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
|
||||
{
|
||||
TimeKeeper load_timer;
|
||||
@@ -336,14 +466,32 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMa
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
|
||||
Mesh* mesh = new Mesh(object_parent_settings ? object_parent_settings : meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if (loadMeshSTL(mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
log("loading '%s' took %.3f seconds\n",filename,load_timer.restart());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
else if (ext && (strcmp(ext, ".obj") == 0 || strcmp(ext, ".OBJ") == 0))
|
||||
{
|
||||
TexturedMesh* mesh = new TexturedMesh(object_parent_settings ? object_parent_settings : meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if (loadMeshOBJ(mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mesh;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "textureProcessing/TexturedMesh.h"
|
||||
#include "ExtruderTrain.h"
|
||||
|
||||
namespace cura
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
|
||||
const ExtruderTrain* getExtruderTrain(unsigned int extruder_nr) const;
|
||||
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<Mesh*> meshes;
|
||||
|
||||
Point3 min() const; //! minimal corner of bounding box
|
||||
Point3 max() const; //! maximal corner of bounding box
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef POLYGON_FLOW_ADJUSTER_H
|
||||
#define POLYGON_FLOW_ADJUSTER_H
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/polygon.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Class for computing and compensating the flow of line segments in a polygon.
|
||||
*
|
||||
*/
|
||||
class PolygonFlowAdjuster
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Compute the flow for a given line segment in the polygons
|
||||
*
|
||||
* \warning should only be called once for each line segment in a polygon!
|
||||
*
|
||||
* \param from the polygons from which to get the segment of a flow, which should be the same polygons as the ones which the PolygonFlowAdjuster was constructed with
|
||||
* \param poly_idx Index to the polygon in which to find the line segment
|
||||
* \param from_point_idx The index to the beginning of the line segment
|
||||
* \param to_point_idx The index to the ending of the line segment
|
||||
* \return a value between zero and one representing the reduced flow of the line segment
|
||||
*/
|
||||
virtual float getFlow(const Polygons& from, unsigned int poly_idx, unsigned int from_point_idx, unsigned int to_point_idx) = 0;
|
||||
|
||||
virtual ~PolygonFlowAdjuster()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
|
||||
#endif//POLYGON_FLOW_ADJUSTER_H
|
||||
@@ -179,6 +179,7 @@ void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcodeLayer, const int laye
|
||||
|
||||
gcodeLayer.addPolygonsByOptimizer(pattern.polygons, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
|
||||
|
||||
last_prime_tower_poly_printed[extruder] = layer_nr;
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -58,10 +58,6 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
|
||||
constexpr int smallest_line_length = 200;
|
||||
constexpr int largest_error_of_removed_point = 50;
|
||||
first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
|
||||
if (first_layer_outline.size() == 0)
|
||||
{
|
||||
logError("Couldn't generate skirt / brim! No polygons on first layer.");
|
||||
}
|
||||
}
|
||||
|
||||
int SkirtBrim::generatePrimarySkirtBrimLines(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const int64_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder)
|
||||
@@ -77,7 +73,7 @@ int SkirtBrim::generatePrimarySkirtBrimLines(SliceDataStorage& storage, int star
|
||||
//Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
|
||||
for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
|
||||
{
|
||||
double area = PolygonRef{outer_skirt_brim_line[n]}.area();
|
||||
double area = outer_skirt_brim_line[n].area();
|
||||
if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
|
||||
{
|
||||
outer_skirt_brim_line.remove(n--);
|
||||
|
||||
@@ -12,12 +12,6 @@ WallsComputation::WallsComputation(int wall_0_inset, int line_width_0, int line_
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateInsets only reads and writes data for the current layer
|
||||
*/
|
||||
void WallsComputation::generateInsets(SliceLayerPart* part)
|
||||
{
|
||||
if (insetCount == 0)
|
||||
@@ -64,12 +58,6 @@ void WallsComputation::generateInsets(SliceLayerPart* part)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateInsets only reads and writes data for the current layer
|
||||
*/
|
||||
void WallsComputation::generateInsets(SliceLayer* layer)
|
||||
{
|
||||
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
|
||||
|
||||
+8
-8
@@ -23,9 +23,9 @@ void Weaver::weave(MeshGroup* meshgroup)
|
||||
|
||||
std::vector<cura::Slicer*> slicerList;
|
||||
|
||||
for(Mesh& mesh : meshgroup->meshes)
|
||||
for (Mesh* mesh : meshgroup->meshes)
|
||||
{
|
||||
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
cura::Slicer* slicer = new cura::Slicer(mesh, initial_layer_thickness, connectionHeight, layer_count, mesh->getSettingBoolean("meshfix_keep_open_polygons"), mesh->getSettingBoolean("meshfix_extensive_stitching"), nullptr);
|
||||
slicerList.push_back(slicer);
|
||||
}
|
||||
|
||||
@@ -209,11 +209,11 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
|
||||
std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts();
|
||||
for (PolygonsPart& roof_part : roof_parts)
|
||||
{
|
||||
roof_outlines.add(PolygonRef{roof_part[0]});
|
||||
roof_outlines.add(roof_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
|
||||
{
|
||||
roof_holes.add(PolygonRef{roof_part[hole_idx]});
|
||||
PolygonRef{roof_holes.back()}.reverse();
|
||||
roof_holes.add(roof_part[hole_idx]);
|
||||
roof_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
|
||||
|
||||
std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts();
|
||||
for (PolygonsPart& supporting_part : supporting_parts)
|
||||
supporting_outlines.add(PolygonRef{supporting_part[0]}); // only add outlines, not the holes
|
||||
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
|
||||
|
||||
|
||||
|
||||
@@ -274,10 +274,10 @@ void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int dir
|
||||
Polygons floor_holes;
|
||||
for (PolygonsPart& floor_part : floor_parts)
|
||||
{
|
||||
floor_outlines.add(PolygonRef{floor_part[0]});
|
||||
floor_outlines.add(floor_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
|
||||
{
|
||||
floor_holes.add(PolygonRef{floor_part[hole_idx]});
|
||||
floor_holes.add(floor_part[hole_idx]);
|
||||
//floor_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
#include "settings/settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygon.h"
|
||||
|
||||
@@ -247,7 +247,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
|
||||
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
|
||||
double top_retract_pause = 2.0;
|
||||
int retract_hop_dist = 1000;
|
||||
coord_t retract_hop_dist = 1000;
|
||||
bool after_retract_hop = false;
|
||||
//bool go_horizontal_first = true;
|
||||
bool lower_retract_start = true;
|
||||
|
||||
+17
-17
@@ -11,7 +11,7 @@
|
||||
#include "settings/settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
#include "slicer/Slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "Weaver.h"
|
||||
@@ -29,37 +29,37 @@ private:
|
||||
static const int STRATEGY_KNOT = 1;
|
||||
static const int STRATEGY_RETRACT = 2;
|
||||
|
||||
int initial_layer_thickness;
|
||||
int filament_diameter;
|
||||
int line_width;
|
||||
coord_t initial_layer_thickness;
|
||||
coord_t filament_diameter;
|
||||
coord_t line_width;
|
||||
double flowConnection;
|
||||
double flowFlat;
|
||||
double extrusion_mm3_per_mm_connection;
|
||||
double extrusion_mm3_per_mm_flat;
|
||||
int nozzle_outer_diameter;
|
||||
int nozzle_head_distance;
|
||||
coord_t nozzle_outer_diameter;
|
||||
coord_t nozzle_head_distance;
|
||||
double nozzle_expansion_angle;
|
||||
int nozzle_clearance;
|
||||
int nozzle_top_diameter;
|
||||
coord_t nozzle_clearance;
|
||||
coord_t nozzle_top_diameter;
|
||||
double moveSpeed;
|
||||
double speedBottom;
|
||||
double speedUp;
|
||||
double speedDown;
|
||||
double speedFlat;
|
||||
int connectionHeight;
|
||||
int roof_inset;
|
||||
coord_t connectionHeight;
|
||||
coord_t roof_inset;
|
||||
double flat_delay;
|
||||
double bottom_delay;
|
||||
double top_delay;
|
||||
int up_dist_half_speed;
|
||||
int top_jump_dist;
|
||||
int fall_down;
|
||||
int drag_along;
|
||||
coord_t up_dist_half_speed;
|
||||
coord_t top_jump_dist;
|
||||
coord_t fall_down;
|
||||
coord_t drag_along;
|
||||
int strategy;
|
||||
double go_back_to_last_top;
|
||||
int straight_first_when_going_down;
|
||||
int roof_fall_down;
|
||||
int roof_drag_along;
|
||||
coord_t straight_first_when_going_down;
|
||||
coord_t roof_fall_down;
|
||||
coord_t roof_drag_along;
|
||||
double roof_outer_delay;
|
||||
|
||||
RetractionConfig standard_retraction_config; //!< The standard retraction settings used for moves between parts etc.
|
||||
|
||||
+5
-5
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace cura {
|
||||
|
||||
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
|
||||
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
|
||||
{
|
||||
AABB boundaryBox(outline);
|
||||
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
|
||||
@@ -29,9 +29,9 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
|
||||
for(unsigned int n=0; n<islands.size(); n++)
|
||||
{
|
||||
//Skip internal holes
|
||||
if (!PolygonRef{islands[n]}.orientation())
|
||||
if (!islands[n].orientation())
|
||||
continue;
|
||||
double area = fabs(PolygonRef{islands[n]}.area());
|
||||
double area = fabs(islands[n].area());
|
||||
if (area > area1)
|
||||
{
|
||||
if (area1 > area2)
|
||||
@@ -51,8 +51,8 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
|
||||
if (idx1 < 0 || idx2 < 0)
|
||||
return -1;
|
||||
|
||||
Point center1 = PolygonRef{islands[idx1]}.centerOfMass();
|
||||
Point center2 = PolygonRef{islands[idx2]}.centerOfMass();
|
||||
Point center1 = islands[idx1].centerOfMass();
|
||||
Point center2 = islands[idx2].centerOfMass();
|
||||
|
||||
return angle(center2 - center1);
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace cura {
|
||||
class Polygons;
|
||||
class SliceLayer;
|
||||
|
||||
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer);
|
||||
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
/*!
|
||||
* Adds closed polygon to the current path
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef poly, int width);
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Polygon poly, int width);
|
||||
private:
|
||||
/*!
|
||||
* Convert and add a point to the points buffer, each point being represented as two consecutive floats. All members adding a 2D point to the data should use this function.
|
||||
@@ -346,9 +346,9 @@ void CommandSocket::connect(const std::string& ip, int port)
|
||||
continue;
|
||||
}
|
||||
const ExtruderTrain* settings_base = meshgroup->getExtruderTrain(extruder_nr); //The extruder train that the setting should fall back to.
|
||||
for (Mesh& mesh : meshgroup->meshes)
|
||||
for (Mesh* mesh : meshgroup->meshes)
|
||||
{
|
||||
mesh.setSettingInheritBase(setting_extruder.name(), *settings_base);
|
||||
mesh->setSettingInheritBase(setting_extruder.name(), *settings_base);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,8 +455,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list, const google
|
||||
}
|
||||
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
|
||||
|
||||
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = meshgroup->meshes.back();
|
||||
meshgroup->meshes.push_back(new Mesh(extruder_train)); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = *meshgroup->meshes.back();
|
||||
|
||||
for (int i = 0; i < face_count; ++i)
|
||||
{
|
||||
@@ -522,7 +522,7 @@ void CommandSocket::sendPolygons(PrintFeatureType type, const Polygons& polygons
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygon(PrintFeatureType type, ConstPolygonRef polygon, int line_width)
|
||||
void CommandSocket::sendPolygon(PrintFeatureType type, Polygon& polygon, int line_width)
|
||||
{
|
||||
#ifdef ARCUS
|
||||
if (CommandSocket::isInstantiated())
|
||||
@@ -797,7 +797,7 @@ void CommandSocket::PathCompiler::sendLineTo(PrintFeatureType print_feature_type
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef polygon, int width)
|
||||
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, Polygon polygon, int width)
|
||||
{
|
||||
if (polygon.size() < 2)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
/*!
|
||||
* Send a polygon to the front-end. This is used for the layerview in the GUI
|
||||
*/
|
||||
static void sendPolygon(cura::PrintFeatureType type, ConstPolygonRef polygon, int line_width);
|
||||
static void sendPolygon(cura::PrintFeatureType type, Polygon& polygon, int line_width);
|
||||
|
||||
/*!
|
||||
* Send a line to the front-end. This is used for the layerview in the GUI
|
||||
|
||||
+12
-11
@@ -51,13 +51,13 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
|
||||
|
||||
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
|
||||
|
||||
for (const Mesh& mesh : meshgroup->meshes)
|
||||
for (const Mesh* mesh : meshgroup->meshes)
|
||||
{
|
||||
if (!mesh.getSettingBoolean("anti_overhang_mesh")
|
||||
&& !mesh.getSettingBoolean("support_mesh")
|
||||
if (!mesh->getSettingBoolean("anti_overhang_mesh")
|
||||
&& !mesh->getSettingBoolean("support_mesh")
|
||||
)
|
||||
{
|
||||
extruder_attr[mesh.getSettingAsIndex("extruder_nr")].is_used = true;
|
||||
extruder_attr[mesh->getSettingAsIndex("extruder_nr")].is_used = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +69,11 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
|
||||
{
|
||||
extruder_attr[extruder_nr].is_used = true;
|
||||
}
|
||||
for (const Mesh& mesh : meshgroup->meshes)
|
||||
for (const Mesh* mesh : meshgroup->meshes)
|
||||
{
|
||||
if ((mesh.getSettingBoolean("support_enable") && mesh.getSettingBoolean("support_interface_enable") && meshgroup->getSettingAsIndex("support_interface_extruder_nr") == int(extruder_nr))
|
||||
|| (mesh.getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_infill_extruder_nr") == int(extruder_nr))
|
||||
|| (mesh.getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_extruder_nr_layer_0") == int(extruder_nr))
|
||||
if ((mesh->getSettingBoolean("support_enable") && mesh->getSettingBoolean("support_interface_enable") && meshgroup->getSettingAsIndex("support_interface_extruder_nr") == int(extruder_nr))
|
||||
|| (mesh->getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_infill_extruder_nr") == int(extruder_nr))
|
||||
|| (mesh->getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_extruder_nr_layer_0") == int(extruder_nr))
|
||||
)
|
||||
{
|
||||
extruder_attr[extruder_nr].is_used = true;
|
||||
@@ -556,6 +556,8 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
|
||||
#endif //ASSERT_INSANE_OUTPUT
|
||||
|
||||
total_bounding_box.include(Point3(x, y, z));
|
||||
|
||||
if (extrusion_mm3_per_mm < 0)
|
||||
logWarning("Warning! Negative extrusion move!");
|
||||
|
||||
@@ -568,7 +570,6 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
|
||||
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
|
||||
|
||||
Point gcode_pos = getGcodePos(x,y, current_extruder);
|
||||
total_bounding_box.include(Point3(gcode_pos.X, gcode_pos.Y, z));
|
||||
|
||||
if (extrusion_mm3_per_mm > 0.000001)
|
||||
{
|
||||
@@ -728,7 +729,7 @@ void GCodeExport::writeZhopStart(int hop_height)
|
||||
{
|
||||
isZHopped = hop_height;
|
||||
*output_stream << "G1 Z" << MMtoStream{currentPosition.z + isZHopped} << new_line;
|
||||
total_bounding_box.includeZ(currentPosition.z + isZHopped);
|
||||
total_bounding_box.include(currentPosition + Point3(0, 0, isZHopped));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,7 +776,7 @@ void GCodeExport::switchExtruder(int new_extruder, const RetractionConfig& retra
|
||||
|
||||
bool force = true;
|
||||
bool extruder_switch = true;
|
||||
writeRetraction(retraction_config_old_extruder, force, extruder_switch);
|
||||
writeRetraction(const_cast<RetractionConfig&>(retraction_config_old_extruder), force, extruder_switch);
|
||||
|
||||
resetExtrusionValue(); // zero the E value on the old extruder, so that the current_e_value is registered on the old extruder
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "MeshGroup.h"
|
||||
#include "commandSocket.h"
|
||||
#include "RetractionConfig.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -91,9 +90,6 @@ private:
|
||||
std::string machine_name;
|
||||
|
||||
std::ostream* output_stream;
|
||||
#ifdef _OPENMP
|
||||
omp_nest_lock_type output_stream_lock;
|
||||
#endif
|
||||
std::string new_line;
|
||||
|
||||
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
|
||||
@@ -181,13 +177,6 @@ public:
|
||||
|
||||
void setOutputStream(std::ostream* stream);
|
||||
|
||||
#ifdef _OPENMP
|
||||
omp_nest_lock_type& getOutputStreamLock()
|
||||
{
|
||||
return output_stream_lock;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool getExtruderIsUsed(const int extruder_nr) const; //!< Returns whether the extruder with the given index is used up until the current meshgroup
|
||||
|
||||
int getNozzleSize(const int extruder_nr) const;
|
||||
|
||||
+31
-48
@@ -11,8 +11,7 @@ namespace cura {
|
||||
|
||||
|
||||
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
|
||||
: is_paths_vector_initialised(false)
|
||||
, extruder(extruder)
|
||||
: extruder(extruder)
|
||||
, heated_pre_travel_time(0)
|
||||
, initial_printing_temperature(-1)
|
||||
, printing_temperature(-1)
|
||||
@@ -55,24 +54,11 @@ double ExtruderPlan::getFanSpeed()
|
||||
{
|
||||
return fan_speed;
|
||||
}
|
||||
void ExtruderPlan::convertListToVector()
|
||||
{
|
||||
unsigned int number_of_paths = paths_list.size();
|
||||
paths_vector.reserve(number_of_paths);
|
||||
for (auto path : paths_list)
|
||||
{
|
||||
if (path.points.size())
|
||||
{
|
||||
paths_vector.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
is_paths_vector_initialised = true;
|
||||
}
|
||||
|
||||
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
{
|
||||
std::list<GCodePath>& paths = extruder_plans.back().getPathsList();
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow) // spiralize can only change when a travel path is in between
|
||||
return &paths.back();
|
||||
paths.emplace_back();
|
||||
@@ -90,9 +76,9 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(const GCodePathConfig* config,
|
||||
|
||||
void GCodePlanner::forceNewPathStart()
|
||||
{
|
||||
std::list<GCodePath>& paths = extruder_plans.back().getPathsList();
|
||||
std::vector<GCodePath>& paths = extruder_plans.back().paths;
|
||||
if (paths.size() > 0)
|
||||
paths.back().done = true;
|
||||
paths[paths.size()-1].done = true;
|
||||
}
|
||||
|
||||
GCodePlanner::GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
@@ -107,7 +93,6 @@ GCodePlanner::GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int l
|
||||
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(current_extruder))
|
||||
, comb_boundary_inside(computeCombBoundaryInside(combing_mode))
|
||||
, fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder)
|
||||
, gcode_written(0)
|
||||
{
|
||||
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
|
||||
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
|
||||
@@ -204,7 +189,7 @@ bool GCodePlanner::setExtruder(int extruder)
|
||||
}
|
||||
addTravel(end_pos); // + extruder_offset cause it
|
||||
}
|
||||
if (extruder_plans.back().getPathsList().empty() && extruder_plans.back().inserts.empty())
|
||||
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
|
||||
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
|
||||
extruder_plans.back().extruder = extruder;
|
||||
}
|
||||
@@ -265,7 +250,7 @@ GCodePath& GCodePlanner::addTravel(Point p)
|
||||
|
||||
const bool perform_z_hops = extr->getSettingBoolean("retraction_hop_enabled");
|
||||
|
||||
const bool is_first_travel_of_extruder_after_switch = extruder_plans.back().getPathsList().size() == 0 && (extruder_plans.size() > 1 || last_extruder_previous_layer != getExtruder());
|
||||
const bool is_first_travel_of_extruder_after_switch = extruder_plans.back().paths.size() == 0 && (extruder_plans.size() > 1 || last_extruder_previous_layer != getExtruder());
|
||||
const bool bypass_combing = is_first_travel_of_extruder_after_switch && extr->getSettingBoolean("retraction_hop_after_extruder_switch");
|
||||
|
||||
if (comb != nullptr && !bypass_combing && lastPosition != no_point)
|
||||
@@ -364,27 +349,31 @@ void GCodePlanner::planPrime()
|
||||
forceNewPathStart();
|
||||
}
|
||||
|
||||
void GCodePlanner::addExtrusionMove(Point p, const GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
|
||||
{
|
||||
getLatestPathWithConfig(config, space_fill_type, flow, spiralize)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygon(ConstPolygonRef polygon, int start_idx, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize)
|
||||
void GCodePlanner::addPolygon(Polygons& polygons, unsigned int poly_idx, int start_idx, GCodePathConfig* config, PolygonFlowAdjuster* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize)
|
||||
{
|
||||
Point p0 = polygon[start_idx];
|
||||
PolygonRef polygon = polygons[poly_idx];
|
||||
unsigned int p0_idx = start_idx;
|
||||
Point p0 = polygon[p0_idx];
|
||||
addTravel(p0);
|
||||
for (unsigned int point_idx = 1; point_idx < polygon.size(); point_idx++)
|
||||
{
|
||||
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
|
||||
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
|
||||
unsigned int p1_idx = (start_idx + point_idx) % polygon.size();
|
||||
Point p1 = polygon[p1_idx];
|
||||
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(polygons, poly_idx, p0_idx, p1_idx) : 1.0;
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
|
||||
p0 = p1;
|
||||
p0_idx = p1_idx;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
{
|
||||
const Point& p1 = polygon[start_idx];
|
||||
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
|
||||
Point& p1 = polygon[start_idx];
|
||||
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(polygons, poly_idx, p0_idx, start_idx) : 1.0;
|
||||
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
|
||||
|
||||
if (wall_0_wipe_dist > 0)
|
||||
@@ -418,7 +407,7 @@ void GCodePlanner::addPolygon(ConstPolygonRef polygon, int start_idx, const GCod
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePlanner::addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, Point z_seam_pos, coord_t wall_0_wipe_dist, bool spiralize)
|
||||
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, PolygonFlowAdjuster* flow_adjuster, EZSeamType z_seam_type, Point z_seam_pos, coord_t wall_0_wipe_dist, bool spiralize)
|
||||
{
|
||||
if (polygons.size() == 0)
|
||||
{
|
||||
@@ -432,10 +421,10 @@ void GCodePlanner::addPolygonsByOptimizer(const Polygons& polygons, const GCodeP
|
||||
orderOptimizer.optimize();
|
||||
for (unsigned int poly_idx : orderOptimizer.polyOrder)
|
||||
{
|
||||
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, wall_0_wipe_dist, spiralize);
|
||||
addPolygon(polygons, poly_idx, orderOptimizer.polyStart[poly_idx], config, flow_adjuster, wall_0_wipe_dist, spiralize);
|
||||
}
|
||||
}
|
||||
void GCodePlanner::addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
|
||||
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
|
||||
{
|
||||
LineOrderOptimizer orderOptimizer(lastPosition);
|
||||
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
|
||||
@@ -445,12 +434,12 @@ void GCodePlanner::addLinesByOptimizer(const Polygons& polygons, const GCodePath
|
||||
orderOptimizer.optimize();
|
||||
for (int poly_idx : orderOptimizer.polyOrder)
|
||||
{
|
||||
ConstPolygonRef polygon = polygons[poly_idx];
|
||||
PolygonRef polygon = polygons[poly_idx];
|
||||
int start = orderOptimizer.polyStart[poly_idx];
|
||||
int end = 1 - start;
|
||||
const Point& p0 = polygon[start];
|
||||
Point& p0 = polygon[start];
|
||||
addTravel(p0);
|
||||
const Point& p1 = polygon[end];
|
||||
Point& p1 = polygon[end];
|
||||
addExtrusionMove(p1, config, space_fill_type);
|
||||
if (wipe_dist != 0)
|
||||
{
|
||||
@@ -473,7 +462,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
|
||||
if (minExtrudeTime < 1)
|
||||
minExtrudeTime = 1;
|
||||
double factor = extrudeTime / minExtrudeTime;
|
||||
for (GCodePath& path : getPathsList())
|
||||
for (GCodePath& path : paths)
|
||||
{
|
||||
if (path.isTravelPath())
|
||||
continue;
|
||||
@@ -497,7 +486,7 @@ void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, do
|
||||
|
||||
// Adjust stored naive time estimates
|
||||
estimates.extrude_time *= inv_factor;
|
||||
for (GCodePath& path : getPathsList())
|
||||
for (GCodePath& path : paths)
|
||||
{
|
||||
path.estimates.extrude_time *= inv_factor;
|
||||
}
|
||||
@@ -515,7 +504,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
|
||||
Point p0 = start_position;
|
||||
|
||||
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
|
||||
for (GCodePath& path : getPathsList())
|
||||
for (GCodePath& path : paths)
|
||||
{
|
||||
bool is_extrusion_path = false;
|
||||
double* path_time_estimate;
|
||||
@@ -668,7 +657,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
gcode.setZ(z);
|
||||
|
||||
|
||||
const GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
|
||||
GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
|
||||
|
||||
int extruder = gcode.getExtruderNr();
|
||||
bool acceleration_enabled = storage.getSettingBoolean("acceleration_enabled");
|
||||
@@ -717,7 +706,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
gcode.writeRetraction(retraction_config);
|
||||
}
|
||||
gcode.writeFanCommand(extruder_plan.getFanSpeed());
|
||||
std::vector<GCodePath>& paths = extruder_plan.getPaths();
|
||||
std::vector<GCodePath>& paths = extruder_plan.paths;
|
||||
|
||||
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
|
||||
return a.path_idx < b.path_idx;
|
||||
@@ -890,9 +879,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
|
||||
} // extruder plans /\ .
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
#pragma omp flush
|
||||
#pragma omp atomic update
|
||||
++gcode_written;
|
||||
}
|
||||
|
||||
void GCodePlanner::overrideFanSpeeds(double speed)
|
||||
@@ -915,7 +901,6 @@ void GCodePlanner::completeConfigs()
|
||||
|
||||
mesh.insetX_config.setLayerHeight(layer_thickness);
|
||||
mesh.skin_config.setLayerHeight(layer_thickness);
|
||||
mesh.perimeter_gap_config.setLayerHeight(layer_thickness);
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setLayerHeight(layer_thickness);
|
||||
@@ -974,7 +959,6 @@ void GCodePlanner::processInitialLayersSpeedup()
|
||||
|
||||
//Skin speed (per mesh).
|
||||
mesh.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
mesh.perimeter_gap_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
|
||||
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
@@ -996,7 +980,6 @@ void GCodePlanner::processInitialLayersSpeedup()
|
||||
mesh.inset0_config.setSpeedIconic();
|
||||
mesh.insetX_config.setSpeedIconic();
|
||||
mesh.skin_config.setSpeedIconic();
|
||||
mesh.perimeter_gap_config.setSpeedIconic();
|
||||
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].setSpeedIconic();
|
||||
@@ -1009,7 +992,7 @@ void GCodePlanner::processInitialLayersSpeedup()
|
||||
|
||||
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
|
||||
{
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].getPaths();
|
||||
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
|
||||
for (unsigned int path_idx2 = path_idx + 1; path_idx2 < paths.size(); path_idx2++)
|
||||
{
|
||||
if (paths[path_idx2].getExtrusionMM3perMM() > 0)
|
||||
@@ -1040,7 +1023,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
|
||||
return false;
|
||||
}
|
||||
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
|
||||
std::vector<GCodePath>& paths = extruder_plan.getPaths();
|
||||
std::vector<GCodePath>& paths = extruder_plan.paths;
|
||||
GCodePath& path = paths[path_idx];
|
||||
if (path_idx + 1 >= paths.size()
|
||||
||
|
||||
|
||||
+13
-59
@@ -11,7 +11,7 @@
|
||||
#include "pathPlanning/TimeMaterialEstimates.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "wallOverlap.h"
|
||||
#include "PolygonFlowAdjuster.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "SpaceFillType.h"
|
||||
@@ -37,12 +37,9 @@ class ExtruderPlan
|
||||
friend class GCodePlanner; // TODO: GCodePlanner still does a lot which should actually be handled in this class.
|
||||
friend class LayerPlanBuffer; // TODO: LayerPlanBuffer handles paths directly
|
||||
protected:
|
||||
std::list<GCodePath> paths_list; //!< The paths planned for this extruder
|
||||
std::vector<GCodePath> paths_vector; //!< The paths planned for this extruder
|
||||
std::vector<GCodePath> paths; //!< The paths planned for this extruder
|
||||
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
|
||||
|
||||
bool is_paths_vector_initialised; //!< Keeps information if content of \p paths_list has been copied to \p paths_vector
|
||||
|
||||
int extruder; //!< The extruder used for this paths in the current plan.
|
||||
double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature
|
||||
double initial_printing_temperature; //!< The required temperature at the start of this extruder plan.
|
||||
@@ -104,7 +101,7 @@ public:
|
||||
while ( ! inserts.empty() )
|
||||
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
|
||||
NozzleTempInsert& insert = inserts.front();
|
||||
assert(insert.path_idx == getPaths().size());
|
||||
assert(insert.path_idx == paths.size());
|
||||
insert.write(gcode);
|
||||
inserts.pop_front();
|
||||
}
|
||||
@@ -159,39 +156,6 @@ public:
|
||||
* \return The fan speed computed in processFanSpeedAndMinimalLayerTime
|
||||
*/
|
||||
double getFanSpeed();
|
||||
|
||||
/*!
|
||||
* Move the paths data from the input list to the vector container
|
||||
*
|
||||
* \warning empties the \p paths_list which will no longer contain data. No references to the paths in \p paths_list should be kept.
|
||||
*/
|
||||
void convertListToVector();
|
||||
|
||||
/*!
|
||||
* Get the paths in a list container
|
||||
*
|
||||
* \warning should not be called after paths_list has been converted to paths variable
|
||||
*
|
||||
* \return The paths as a list
|
||||
*/
|
||||
std::list<GCodePath>& getPathsList()
|
||||
{
|
||||
assert(!is_paths_vector_initialised);
|
||||
return paths_list;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the paths in a vector container
|
||||
*
|
||||
* \warning should not be called before paths_list has been converted to paths variable
|
||||
*
|
||||
* \return The paths as a vector
|
||||
*/
|
||||
std::vector<GCodePath>& getPaths()
|
||||
{
|
||||
assert(is_paths_vector_initialised);
|
||||
return paths_vector;
|
||||
}
|
||||
protected:
|
||||
|
||||
Point start_position; //!< The position the print head was at at the start of this extruder plan
|
||||
@@ -276,8 +240,6 @@ private:
|
||||
|
||||
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
|
||||
|
||||
int gcode_written;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Either create a new path with the given config or return the last path if it already had that config.
|
||||
@@ -289,7 +251,7 @@ private:
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
* \return A path with the given config which is now the last path in GCodePlanner::paths
|
||||
*/
|
||||
GCodePath* getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
public:
|
||||
/*!
|
||||
@@ -415,18 +377,19 @@ public:
|
||||
* \param flow A modifier of the extrusion width which would follow from the \p config
|
||||
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
|
||||
*/
|
||||
void addExtrusionMove(Point p, const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add polygon to the gcode starting at vertex \p startIdx
|
||||
* \param polygon The polygon
|
||||
* \param polygon The polygons from which to get the polygon
|
||||
* \param polygon The index of the polygon in \p polygons
|
||||
* \param startIdx The index of the starting vertex of the \p polygon
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param flow_adjuster Construct yielding the flow of each segment added (optionally nullptr)
|
||||
* \param wall_0_wipe_dist The distance to travel along the polygon after it has been laid down, in order to wipe the start and end of the wall together
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
|
||||
*/
|
||||
void addPolygon(ConstPolygonRef polygon, int startIdx, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
|
||||
void addPolygon(Polygons& polygons, unsigned int poly_idx, int startIdx, GCodePathConfig* config, PolygonFlowAdjuster* flow_adjuster = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add polygons to the gcode with optimized order.
|
||||
@@ -439,13 +402,13 @@ public:
|
||||
*
|
||||
* \param polygons The polygons
|
||||
* \param config The config with which to print the polygon lines
|
||||
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
|
||||
* \param flow_adjuster Construct yielding the flow of each segment added (optionally nullptr)
|
||||
* \param z_seam_type The seam type / poly start optimizer
|
||||
* \param z_seam_pos The location near where to start each part in case \p z_seam_type is 'back'
|
||||
* \param wall_0_wipe_dist The distance to travel along each polygon after it has been laid down, in order to wipe the start and end of the wall together
|
||||
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
|
||||
*/
|
||||
void addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false);
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, PolygonFlowAdjuster* flow_adjuster = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false);
|
||||
|
||||
/*!
|
||||
* Add lines to the gcode with optimized order.
|
||||
@@ -454,7 +417,7 @@ public:
|
||||
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
|
||||
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
|
||||
*/
|
||||
void addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
|
||||
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
|
||||
|
||||
/*!
|
||||
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
|
||||
@@ -472,16 +435,7 @@ public:
|
||||
* \param gcode The gcode to write the planned paths to
|
||||
*/
|
||||
void writeGCode(GCodeExport& gcode);
|
||||
/*!
|
||||
* Has the planned paths been written to gcode
|
||||
*/
|
||||
int isGCodeWritten()
|
||||
{
|
||||
int gcode_written_tmp;
|
||||
#pragma omp atomic read
|
||||
gcode_written_tmp = gcode_written;
|
||||
return gcode_written_tmp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Complete all GcodePathConfigs by
|
||||
* - altering speeds to conform to speed_print_layer_0 and
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ int Infill::computeScanSegmentIdx(int x, int line_width)
|
||||
return x / line_width;
|
||||
}
|
||||
|
||||
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh)
|
||||
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh)
|
||||
{
|
||||
if (in_outline.size() == 0) return;
|
||||
if (line_distance == 0) return;
|
||||
@@ -144,7 +144,7 @@ void Infill::generateTriangleInfill(Polygons& result)
|
||||
generateLineInfill(result, line_distance, fill_angle + 120, 0);
|
||||
}
|
||||
|
||||
void Infill::generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh)
|
||||
void Infill::generateCubicSubDivInfill(Polygons& result, SliceMeshStorage& mesh)
|
||||
{
|
||||
Polygons uncropped;
|
||||
mesh.base_subdiv_cube->generateSubdivisionLines(z, uncropped);
|
||||
|
||||
+3
-3
@@ -80,7 +80,7 @@ public:
|
||||
* \param result_lines (output) The resulting line segments (from linear infill types)
|
||||
* \param mesh The mesh for which to geenrate infill (should only be used for non-helper objects)
|
||||
*/
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh = nullptr);
|
||||
void generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh = nullptr);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -148,8 +148,8 @@ private:
|
||||
* \param[out] result The resulting lines
|
||||
* \param[in] mesh Where the Cubic Subdivision Infill precomputation is stored
|
||||
*/
|
||||
void generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh);
|
||||
|
||||
void generateCubicSubDivInfill(Polygons& result, SliceMeshStorage& mesh);
|
||||
|
||||
/*!
|
||||
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
|
||||
* \param result (output) The resulting lines
|
||||
|
||||
@@ -174,7 +174,7 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int dept
|
||||
rel_child_centers.emplace_back(-1, -1, 1);
|
||||
for (Point3 rel_child_center : rel_child_centers)
|
||||
{
|
||||
child_center = center + rotation_matrix.apply(rel_child_center * int32_t(cube_properties.side_length / 4));
|
||||
child_center = center + rotation_matrix.apply(rel_child_center * coord_t(cube_properties.side_length / 4));
|
||||
if (isValidSubdivision(mesh, child_center, radius))
|
||||
{
|
||||
children[child_nr] = new SubDivCube(mesh, child_center, depth - 1);
|
||||
@@ -186,15 +186,15 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int dept
|
||||
bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius)
|
||||
{
|
||||
int64_t distance2;
|
||||
coord_t sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer
|
||||
long int sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer
|
||||
bool inside_somewhere = false;
|
||||
bool outside_somewhere = false;
|
||||
int inside;
|
||||
double part_dist;//what percentage of the radius the target layer is away from the center along the z axis. 0 - 1
|
||||
const coord_t layer_height = mesh.getSettingInMicrons("layer_height");
|
||||
int bottom_layer = (center.z - radius) / layer_height;
|
||||
int top_layer = (center.z + radius) / layer_height;
|
||||
for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain.
|
||||
const long int layer_height = mesh.getSettingInMicrons("layer_height");
|
||||
long int bottom_layer = (center.z - radius) / layer_height;
|
||||
long int top_layer = (center.z + radius) / layer_height;
|
||||
for (long int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain.
|
||||
{
|
||||
part_dist = (double)(test_layer * layer_height - center.z) / radius;
|
||||
sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist));
|
||||
@@ -221,9 +221,9 @@ bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int6
|
||||
return false;
|
||||
}
|
||||
|
||||
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2)
|
||||
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, long int layer_nr, Point& location, int64_t* distance2)
|
||||
{
|
||||
if (layer_nr < 0 || (unsigned int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
|
||||
if (layer_nr < 0 || (unsigned long int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ private:
|
||||
* \param[out] distance2 the squared distance to the infill border
|
||||
* \return Code 0: outside, 1: inside, 2: boundary does not exist at specified layer
|
||||
*/
|
||||
static int distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2);
|
||||
static int distanceFromPointToMesh(SliceMeshStorage& mesh, long int layer_nr, Point& location, int64_t* distance2);
|
||||
|
||||
/*!
|
||||
* Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than epsilon.
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/string.h"
|
||||
@@ -210,7 +211,7 @@ void slice(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
|
||||
last_settings_object = meshgroup->meshes.back();
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
|
||||
+22
-2
@@ -19,12 +19,21 @@ Mesh::Mesh(SettingsBaseVirtual* parent)
|
||||
{
|
||||
}
|
||||
|
||||
void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
bool Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
{
|
||||
int vi0 = findIndexOfVertex(v0);
|
||||
int vi1 = findIndexOfVertex(v1);
|
||||
int vi2 = findIndexOfVertex(v2);
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2) return; // the face has two vertices which get assigned the same location. Don't add the face.
|
||||
return addFace(vi0, vi1, vi2);
|
||||
}
|
||||
|
||||
bool Mesh::addFace(int vi0, int vi1, int vi2)
|
||||
{
|
||||
if (vi0 == vi1 || vi1 == vi2 || vi0 == vi2)
|
||||
{
|
||||
// the face has two vertices which get assigned the same location. Don't add the face.
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = faces.size(); // index of face to be added
|
||||
faces.emplace_back();
|
||||
@@ -35,6 +44,8 @@ void Mesh::addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
vertices[face.vertex_index[0]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[1]].connected_faces.push_back(idx);
|
||||
vertices[face.vertex_index[2]].connected_faces.push_back(idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mesh::clear()
|
||||
@@ -81,6 +92,14 @@ void Mesh::expandXY(int64_t offset)
|
||||
}
|
||||
|
||||
|
||||
void Mesh::addVertex(const Point3& v)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
vertex_hash_map[hash].push_back(vertices.size());
|
||||
vertices.emplace_back(v);
|
||||
aabb.include(v);
|
||||
}
|
||||
|
||||
int Mesh::findIndexOfVertex(const Point3& v)
|
||||
{
|
||||
uint32_t hash = pointHash(v);
|
||||
@@ -189,4 +208,5 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
|
||||
return bestIdx;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+21
-1
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "settings/settings.h"
|
||||
#include "utils/AABB3D.h"
|
||||
#include "textureProcessing/MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -65,7 +66,26 @@ public:
|
||||
|
||||
Mesh(SettingsBaseVirtual* parent); //!< initializes the settings
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
|
||||
virtual ~Mesh() {} //!< Destructor
|
||||
|
||||
/*!
|
||||
* add a face to the mesh without settings it's connected_faces.
|
||||
*
|
||||
* Don't add a face when the surface is zero mm^2
|
||||
*
|
||||
* \return whether a face has been added
|
||||
*/
|
||||
bool addFace(Point3& v0, Point3& v1, Point3& v2);
|
||||
/*!
|
||||
* add a face to the mesh without settings it's connected_faces.
|
||||
*
|
||||
* Don't add a face when the surface is zero mm^2
|
||||
*
|
||||
* \return whether a face has been added
|
||||
*/
|
||||
bool addFace(int vi0, int vi1, int vi2);
|
||||
void addVertex(const Point3& v);
|
||||
|
||||
void clear(); //!< clears all data
|
||||
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "multithreadOpenMP.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
bool abort_execution = false;
|
||||
|
||||
#ifdef _OPENMP
|
||||
void handleMultithreadAbort()
|
||||
{
|
||||
if (checkMultithreadAbort())
|
||||
{
|
||||
std::exit(17);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
@@ -1,182 +0,0 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#ifndef MULTITHREAD_OPENMP_H
|
||||
#define MULTITHREAD_OPENMP_H
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
// TODO: remove
|
||||
extern bool abort_execution;
|
||||
|
||||
#ifdef _OPENMP
|
||||
|
||||
class omp_lock_type
|
||||
{
|
||||
public:
|
||||
omp_lock_type()
|
||||
{
|
||||
omp_init_lock(&lock_object);
|
||||
}
|
||||
~omp_lock_type()
|
||||
{
|
||||
omp_destroy_lock(&lock_object);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
omp_set_lock(&lock_object);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
omp_unset_lock(&lock_object);
|
||||
}
|
||||
int test_lock()
|
||||
{
|
||||
return omp_test_lock(&lock_object);
|
||||
}
|
||||
private:
|
||||
omp_lock_t lock_object;
|
||||
omp_lock_type( const omp_lock_type& ) = delete;
|
||||
omp_lock_type& operator=( const omp_lock_type& ) = delete;
|
||||
};
|
||||
|
||||
class omp_nest_lock_type
|
||||
{
|
||||
public:
|
||||
omp_nest_lock_type()
|
||||
{
|
||||
omp_init_nest_lock(&lock_object);
|
||||
}
|
||||
~omp_nest_lock_type()
|
||||
{
|
||||
omp_destroy_nest_lock(&lock_object);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
omp_set_nest_lock(&lock_object);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
omp_unset_nest_lock(&lock_object);
|
||||
}
|
||||
int test_lock()
|
||||
{
|
||||
return omp_test_nest_lock(&lock_object);
|
||||
}
|
||||
private:
|
||||
omp_nest_lock_t lock_object;
|
||||
omp_nest_lock_type( const omp_nest_lock_type& ) = delete;
|
||||
omp_nest_lock_type& operator=( const omp_nest_lock_type& ) = delete;
|
||||
};
|
||||
|
||||
template <typename LockType>
|
||||
class omp_try_lock_guard_t
|
||||
{
|
||||
public:
|
||||
omp_try_lock_guard_t( LockType& lock_)
|
||||
: lock(lock_)
|
||||
{
|
||||
has_lock = lock.test_lock();
|
||||
}
|
||||
~omp_try_lock_guard_t()
|
||||
{
|
||||
if (has_lock)
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
int isLocked()
|
||||
{
|
||||
return has_lock;
|
||||
}
|
||||
private:
|
||||
LockType& lock;
|
||||
int has_lock;
|
||||
omp_try_lock_guard_t( const omp_try_lock_guard_t& ) = delete;
|
||||
omp_try_lock_guard_t<LockType>& operator=( const omp_try_lock_guard_t& ) = delete;
|
||||
};
|
||||
|
||||
template <typename LockType>
|
||||
class omp_lock_guard_t
|
||||
{
|
||||
public:
|
||||
omp_lock_guard_t( LockType& lock_)
|
||||
: lock(lock_)
|
||||
{
|
||||
lock.lock();
|
||||
}
|
||||
~omp_lock_guard_t()
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
private:
|
||||
LockType& lock;
|
||||
omp_lock_guard_t( const omp_lock_guard_t& ) = delete;
|
||||
omp_lock_guard_t& operator=( const omp_lock_guard_t& ) = delete;
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO: remove
|
||||
inline bool checkMultithreadAbort()
|
||||
{
|
||||
bool tmp_abort_execution;
|
||||
#pragma omp atomic read
|
||||
tmp_abort_execution = abort_execution;
|
||||
return tmp_abort_execution;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
inline void setMultithreadAbort()
|
||||
{
|
||||
#pragma omp atomic write
|
||||
abort_execution = true;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
#ifdef _OPENMP
|
||||
void handleMultithreadAbort();
|
||||
#else
|
||||
inline void handleMultithreadAbort(){}
|
||||
#endif
|
||||
|
||||
// TODO: remove old code below
|
||||
#ifdef _OPENMP
|
||||
#define MULTITHREAD_FOR_CATCH_EXCEPTION(code) \
|
||||
if (checkMultithreadAbort()) \
|
||||
{ \
|
||||
continue; \
|
||||
} \
|
||||
try \
|
||||
{ \
|
||||
code \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
setMultithreadAbort(); \
|
||||
}
|
||||
#else
|
||||
#define MULTITHREAD_FOR_CATCH_EXCEPTION(code) code
|
||||
#endif
|
||||
|
||||
#ifdef _OPENMP
|
||||
#define MULTITHREAD_TASK_CATCH_EXCEPTION(code) \
|
||||
if (!checkMultithreadAbort()) \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
code \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
setMultithreadAbort(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define MULTITHREAD_TASK_CATCH_EXCEPTION(code) code
|
||||
#endif
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif // MULTITHREAD_OPENMP_H
|
||||
@@ -16,7 +16,7 @@ void PathOrderOptimizer::optimize()
|
||||
bool picked[polygons.size()];
|
||||
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
|
||||
|
||||
for (ConstPolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = std::numeric_limits<float>::infinity();
|
||||
@@ -102,15 +102,15 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
|
||||
|
||||
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
|
||||
{
|
||||
ConstPolygonRef poly = polygons[poly_idx];
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
|
||||
int best_point_idx = -1;
|
||||
float best_point_score = std::numeric_limits<float>::infinity();
|
||||
Point p0 = poly.back();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
const Point& p1 = poly[point_idx];
|
||||
const Point& p2 = poly[(point_idx + 1) % poly.size()];
|
||||
Point& p1 = poly[point_idx];
|
||||
Point& p2 = poly[(point_idx + 1) % poly.size()];
|
||||
int64_t dist = vSize2(p1 - prev_point);
|
||||
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
|
||||
// this score is in the order of 5 mm
|
||||
@@ -143,7 +143,7 @@ void LineOrderOptimizer::optimize()
|
||||
{
|
||||
int best_point_idx = -1;
|
||||
float best_point_dist = std::numeric_limits<float>::infinity();
|
||||
ConstPolygonRef poly = polygons[poly_idx];
|
||||
PolygonRef poly = polygons[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
|
||||
{
|
||||
float dist = vSize2f(poly[point_idx] - startPoint);
|
||||
@@ -199,13 +199,13 @@ void LineOrderOptimizer::optimize()
|
||||
|
||||
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
|
||||
{
|
||||
ConstPolygonRef best_line = polygons[best_line_idx];
|
||||
PolygonRef best_line = polygons[best_line_idx];
|
||||
assert(best_line.size() == 2);
|
||||
|
||||
int line_start_point_idx = polyStart[best_line_idx];
|
||||
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
|
||||
const Point& line_start = best_line[line_start_point_idx];
|
||||
const Point& line_end = best_line[line_end_point_idx];
|
||||
Point& line_start = best_line[line_start_point_idx];
|
||||
Point& line_end = best_line[line_end_point_idx];
|
||||
prev_point = line_end;
|
||||
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
|
||||
|
||||
@@ -221,8 +221,8 @@ void LineOrderOptimizer::optimize()
|
||||
|
||||
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
|
||||
{
|
||||
const Point& p0 = polygons[poly_idx][0];
|
||||
const Point& p1 = polygons[poly_idx][1];
|
||||
Point& p0 = polygons[poly_idx][0];
|
||||
Point& p1 = polygons[poly_idx][1];
|
||||
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
|
||||
{ /// check distance to first point on line (0)
|
||||
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
EZSeamType type;
|
||||
Point startPoint; //!< A location near the prefered start location
|
||||
Point z_seam_pos; //!< The position near where to create the z_seam (if \ref PathOrderOptimizer::type == 'back')
|
||||
std::vector<ConstPolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
|
||||
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
|
||||
|
||||
@@ -33,15 +33,10 @@ public:
|
||||
|
||||
void addPolygon(PolygonRef polygon)
|
||||
{
|
||||
this->polygons.emplace_back(polygon);
|
||||
this->polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
void addPolygon(ConstPolygonRef polygon)
|
||||
{
|
||||
this->polygons.emplace_back(polygon);
|
||||
}
|
||||
|
||||
void addPolygons(const Polygons& polygons)
|
||||
void addPolygons(Polygons& polygons)
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
this->polygons.push_back(polygons[i]);
|
||||
@@ -71,7 +66,7 @@ class LineOrderOptimizer
|
||||
{
|
||||
public:
|
||||
Point startPoint; //!< The location of the nozzle before starting to print the current layer
|
||||
std::vector<ConstPolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
|
||||
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
|
||||
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
|
||||
|
||||
@@ -85,11 +80,6 @@ public:
|
||||
this->polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
void addPolygon(ConstPolygonRef polygon)
|
||||
{
|
||||
this->polygons.push_back(polygon);
|
||||
}
|
||||
|
||||
void addPolygons(Polygons& polygons)
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "../utils/polygonUtils.h"
|
||||
#include "../utils/PolygonsPointIndex.h"
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "../utils/SVG.h"
|
||||
#include "../utils/linearAlg2D.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
@@ -332,7 +332,7 @@ bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to,
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::Crossing::findBestCrossing(const Polygons& outside, ConstPolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber)
|
||||
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::Crossing::findBestCrossing(const Polygons& outside, const PolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber)
|
||||
{
|
||||
ClosestPolygonPoint* best_in = nullptr;
|
||||
ClosestPolygonPoint* best_out = nullptr;
|
||||
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
Point in_or_mid; //!< The point on the inside boundary, or in between the inside and outside boundary if the start/end point isn't inside the inside boudary
|
||||
Point out; //!< The point on the outside boundary
|
||||
PolygonsPart dest_part; //!< The assembled inside-boundary PolygonsPart in which the dest_point lies. (will only be initialized when Crossing::dest_is_inside holds)
|
||||
std::optional<ConstPolygonRef> dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
|
||||
std::optional<PolygonRef> dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
|
||||
const Polygons& boundary_inside; //!< The inside boundary as in \ref Comb::boundary_inside
|
||||
const LocToLineGrid* inside_loc_to_line; //!< The loc to line grid \ref Comb::inside_loc_to_line
|
||||
|
||||
@@ -97,7 +97,7 @@ private:
|
||||
* \param comber[in] The combing calculator which has references to the offsets and boundaries to use in combing.
|
||||
* \return A pair of which the first is the crossing point on the inside boundary and the second the crossing point on the outside boundary
|
||||
*/
|
||||
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findBestCrossing(const Polygons& outside, ConstPolygonRef from, Point estimated_start, Point estimated_end, Comb& comber);
|
||||
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findBestCrossing(const Polygons& outside, const PolygonRef from, Point estimated_start, Point estimated_end, Comb& comber);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace cura
|
||||
class GCodePath
|
||||
{
|
||||
public:
|
||||
const GCodePathConfig* config; //!< The configuration settings of the path.
|
||||
GCodePathConfig* config; //!< The configuration settings of the path.
|
||||
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
|
||||
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
|
||||
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
|
||||
|
||||
@@ -125,7 +125,7 @@ bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::strin
|
||||
|
||||
int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
|
||||
{
|
||||
if (extruder_train_ids.empty())
|
||||
if (extruder_train_ids.empty()) //... Tough luck, buddy.
|
||||
{
|
||||
logError("Couldn't find any extruder trains!\n");
|
||||
return -1;
|
||||
|
||||
@@ -426,7 +426,7 @@ FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::
|
||||
return FillPerimeterGapMode::NOWHERE;
|
||||
}
|
||||
|
||||
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
|
||||
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "off")
|
||||
@@ -444,7 +444,7 @@ CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
|
||||
return CombingMode::ALL;
|
||||
}
|
||||
|
||||
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key)
|
||||
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "xy_overrides_z")
|
||||
@@ -458,6 +458,32 @@ SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::st
|
||||
return SupportDistPriority::XY_OVERRIDES_Z;
|
||||
}
|
||||
|
||||
ColourUsage SettingsBaseVirtual::getSettingAsColourUsage(std::string key) const
|
||||
{
|
||||
std::string value = getSettingString(key);
|
||||
if (value == "red")
|
||||
{
|
||||
return ColourUsage::RED;
|
||||
}
|
||||
if (value == "green")
|
||||
{
|
||||
return ColourUsage::GREEN;
|
||||
}
|
||||
if (value == "blue")
|
||||
{
|
||||
return ColourUsage::BLUE;
|
||||
}
|
||||
if (value == "alpha")
|
||||
{
|
||||
return ColourUsage::ALPHA;
|
||||
}
|
||||
if (value == "grey")
|
||||
{
|
||||
return ColourUsage::GREY;
|
||||
}
|
||||
return ColourUsage::GREY;
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
|
||||
@@ -177,6 +177,18 @@ enum class SupportDistPriority
|
||||
Z_OVERRIDES_XY
|
||||
};
|
||||
|
||||
/*!
|
||||
* Which color(s) of a texture to use
|
||||
*/
|
||||
enum class ColourUsage
|
||||
{
|
||||
RED = 0,
|
||||
GREEN = 1,
|
||||
BLUE = 2,
|
||||
ALPHA = 3,
|
||||
GREY // use red, green and blue channels
|
||||
};
|
||||
|
||||
#define MAX_EXTRUDERS 16
|
||||
|
||||
//Maximum number of infill layers that can be combined into a single infill extrusion area.
|
||||
@@ -251,8 +263,9 @@ public:
|
||||
EZSeamType getSettingAsZSeamType(std::string key) const;
|
||||
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
|
||||
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key) const;
|
||||
CombingMode getSettingAsCombingMode(std::string key);
|
||||
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
|
||||
CombingMode getSettingAsCombingMode(std::string key) const;
|
||||
SupportDistPriority getSettingAsSupportDistPriority(std::string key) const;
|
||||
ColourUsage getSettingAsColourUsage(std::string key) const;
|
||||
};
|
||||
|
||||
class SettingRegistry;
|
||||
|
||||
+2
-28
@@ -10,16 +10,7 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateSkinAreas reads data from mesh.layers.parts[*].insets and writes to mesh.layers[n].parts[*].skin_parts
|
||||
* generateSkinInsets only read/writes the skin_parts from the current layer.
|
||||
*
|
||||
* generateSkins therefore reads (depends on) data from mesh.layers[*].parts[*].insets and writes mesh.layers[n].parts[*].skin_parts
|
||||
*/
|
||||
|
||||
void generateSkins(int layerNr, SliceMeshStorage& mesh, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_line_width, int insetCount, bool no_small_gaps_heuristic)
|
||||
{
|
||||
generateSkinAreas(layerNr, mesh, innermost_wall_line_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
|
||||
@@ -32,12 +23,6 @@ void generateSkins(int layerNr, SliceMeshStorage& mesh, int downSkinCount, int u
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateSkinAreas reads data from mesh.layers[*].parts[*].insets and writes to mesh.layers[n].parts[*].skin_parts
|
||||
*/
|
||||
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
@@ -121,12 +106,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateSkinInsets only read/writes the skin_parts from the current layer.
|
||||
*/
|
||||
|
||||
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount)
|
||||
{
|
||||
if (insetCount == 0)
|
||||
@@ -159,12 +139,6 @@ void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int ins
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is executed in a parallel region based on layer_nr.
|
||||
* When modifying make sure any changes does not introduce data races.
|
||||
*
|
||||
* generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area
|
||||
*/
|
||||
void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int infill_skin_overlap, int wall_line_count)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layerNr];
|
||||
|
||||
@@ -35,7 +35,7 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
|
||||
{
|
||||
if (external_polys_only)
|
||||
{
|
||||
result.add(part.outline.outerPolygon());
|
||||
result.add(const_cast<SliceLayerPart&>(part).outline.outerPolygon()); // TODO: make a const version of outerPolygon()
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -57,12 +57,12 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
|
||||
{
|
||||
// we want the 2nd inner walls
|
||||
if (part.insets.size() >= 2) {
|
||||
layer_walls.add(part.insets[1]);
|
||||
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[1]); // TODO const cast!
|
||||
continue;
|
||||
}
|
||||
// but we'll also take the inner wall if the 2nd doesn't exist
|
||||
if (part.insets.size() == 1) {
|
||||
layer_walls.add(part.insets[0]);
|
||||
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[0]); // TODO const cast!
|
||||
continue;
|
||||
}
|
||||
// offset_from_outlines was so large that it completely destroyed our isle,
|
||||
@@ -72,12 +72,46 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
|
||||
}
|
||||
}
|
||||
|
||||
SliceMeshStorage::SliceMeshStorage(SettingsBaseVirtual* settings, unsigned int slice_layer_count)
|
||||
: SettingsMessenger(settings)
|
||||
, layer_nr_max_filled_layer(0)
|
||||
, inset0_config(PrintFeatureType::OuterWall)
|
||||
, insetX_config(PrintFeatureType::InnerWall)
|
||||
, skin_config(PrintFeatureType::Skin)
|
||||
, base_subdiv_cube(nullptr)
|
||||
, texture_proximity_processor(nullptr)
|
||||
{
|
||||
layers.resize(slice_layer_count);
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(PrintFeatureType::Infill);
|
||||
}
|
||||
|
||||
|
||||
SliceMeshStorage::SliceMeshStorage(SliceMeshStorage&& old)
|
||||
: SettingsMessenger(SettingsBaseVirtual::parent)
|
||||
, layers(old.layers)
|
||||
, layer_nr_max_filled_layer(old.layer_nr_max_filled_layer)
|
||||
, inset0_config(old.inset0_config)
|
||||
, insetX_config(old.insetX_config)
|
||||
, skin_config(old.skin_config)
|
||||
, base_subdiv_cube(old.base_subdiv_cube)
|
||||
, texture_proximity_processor(old.texture_proximity_processor)
|
||||
{
|
||||
old.base_subdiv_cube = nullptr;
|
||||
old.texture_proximity_processor = nullptr;
|
||||
}
|
||||
|
||||
SliceMeshStorage::~SliceMeshStorage()
|
||||
{
|
||||
if (base_subdiv_cube)
|
||||
{
|
||||
delete base_subdiv_cube;
|
||||
}
|
||||
if (texture_proximity_processor)
|
||||
{
|
||||
delete texture_proximity_processor;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
|
||||
@@ -124,6 +158,11 @@ SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(mes
|
||||
{
|
||||
}
|
||||
|
||||
SliceDataStorage::~SliceDataStorage()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
|
||||
{
|
||||
if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
|
||||
@@ -163,7 +202,7 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
|
||||
}
|
||||
const SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getOutlines(total, external_polys_only);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make all getSetting functions const??
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
}
|
||||
@@ -207,7 +246,7 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
|
||||
{
|
||||
const SliceLayer& layer = mesh.layers[layer_nr];
|
||||
layer.getSecondOrInnermostWalls(total);
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make getSetting const? make settings.setting_values mapping mutable??
|
||||
{
|
||||
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
|
||||
}
|
||||
|
||||
+17
-19
@@ -12,6 +12,7 @@
|
||||
#include "MeshGroup.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "GCodePathConfig.h"
|
||||
#include "textureProcessing/TextureProximityProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -145,7 +146,13 @@ public:
|
||||
|
||||
class SubDivCube; // forward declaration to prevent dependency loop
|
||||
|
||||
class SliceMeshStorage : public SettingsMessenger // passes on settings from a Mesh object
|
||||
/*!
|
||||
*
|
||||
* passes on settings from a Mesh object
|
||||
*
|
||||
* Cannot be copied due to \ref SliceMeshStorage::texture_proximity_processor being governed by this object alone
|
||||
*/
|
||||
class SliceMeshStorage : public SettingsMessenger, public NoCopy
|
||||
{
|
||||
public:
|
||||
std::vector<SliceLayer> layers;
|
||||
@@ -155,25 +162,18 @@ public:
|
||||
GCodePathConfig inset0_config;
|
||||
GCodePathConfig insetX_config;
|
||||
GCodePathConfig skin_config;
|
||||
GCodePathConfig perimeter_gap_config;
|
||||
std::vector<GCodePathConfig> infill_config;
|
||||
|
||||
SubDivCube* base_subdiv_cube;
|
||||
|
||||
SliceMeshStorage(SettingsBaseVirtual* settings, unsigned int slice_layer_count)
|
||||
: SettingsMessenger(settings)
|
||||
, layer_nr_max_filled_layer(0)
|
||||
, inset0_config(PrintFeatureType::OuterWall)
|
||||
, insetX_config(PrintFeatureType::InnerWall)
|
||||
, skin_config(PrintFeatureType::Skin)
|
||||
, perimeter_gap_config(PrintFeatureType::Skin)
|
||||
, base_subdiv_cube(nullptr)
|
||||
{
|
||||
layers.resize(slice_layer_count);
|
||||
infill_config.reserve(MAX_INFILL_COMBINE);
|
||||
for(int n=0; n<MAX_INFILL_COMBINE; n++)
|
||||
infill_config.emplace_back(PrintFeatureType::Infill);
|
||||
}
|
||||
TextureProximityProcessor* texture_proximity_processor; //!< TextureProximityProcessor per layer per mesh (if that mesh needs a proximity processor)
|
||||
|
||||
SliceMeshStorage(SettingsBaseVirtual* settings, unsigned int slice_layer_count);
|
||||
|
||||
/*!
|
||||
* Move constructor
|
||||
*/
|
||||
SliceMeshStorage(SliceMeshStorage&& old);
|
||||
|
||||
virtual ~SliceMeshStorage();
|
||||
};
|
||||
@@ -244,9 +244,7 @@ public:
|
||||
*/
|
||||
SliceDataStorage(MeshGroup* meshgroup);
|
||||
|
||||
~SliceDataStorage()
|
||||
{
|
||||
}
|
||||
~SliceDataStorage();
|
||||
|
||||
/*!
|
||||
* Get all outlines within a given layer.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_CLOSE_POLYGON_RESULT_H
|
||||
#define SLICER_CLOSE_POLYGON_RESULT_H
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ClosePolygonResult
|
||||
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
|
||||
//The line on which the point lays is between pointIdx-1 and pointIdx
|
||||
public:
|
||||
Point intersectionPoint;
|
||||
int polygonIdx = -1;
|
||||
unsigned int pointIdx = -1;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SLICER_CLOSE_POLYGON_RESULT_H
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_GAP_CLOSER_RESULT_H
|
||||
#define SLICER_GAP_CLOSER_RESULT_H
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class GapCloserResult
|
||||
{
|
||||
public:
|
||||
int64_t len = -1;
|
||||
int polygonIdx = -1;
|
||||
unsigned int pointIdxA = -1;
|
||||
unsigned int pointIdxB = -1;
|
||||
bool AtoB = false;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SLICER_GAP_CLOSER_RESULT_H
|
||||
@@ -1,11 +1,10 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "layerPart.h"
|
||||
#include "settings/settings.h"
|
||||
#include "progress/Progress.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
#include "LayerPart.h"
|
||||
#include "../settings/settings.h"
|
||||
#include "../progress/Progress.h"
|
||||
|
||||
#include "utils/SVG.h" // debug output
|
||||
#include "../utils/SVG.h" // debug output
|
||||
|
||||
/*
|
||||
The layer-part creation step is the first step in creating actual useful data for 3D printing.
|
||||
@@ -29,8 +28,8 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
|
||||
{
|
||||
for(unsigned int i=0; i<layer->polygons.size(); i++)
|
||||
{
|
||||
if (PolygonRef{layer->polygons[i]}.orientation())
|
||||
PolygonRef{layer->polygons[i]}.reverse();
|
||||
if (layer->polygons[i].orientation())
|
||||
layer->polygons[i].reverse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,17 +44,12 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
|
||||
}
|
||||
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
|
||||
{
|
||||
const auto total_layers = slicer->layers.size();
|
||||
// mesh.layers.resize(total_layers); TODO: put this back?
|
||||
assert(mesh.layers.size() == total_layers);
|
||||
#pragma omp parallel for default(none) shared(mesh,slicer) firstprivate(union_layers,union_all_remove_holes) schedule(dynamic)
|
||||
for(unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
|
||||
{ MULTITHREAD_FOR_CATCH_EXCEPTION(
|
||||
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
|
||||
{
|
||||
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
|
||||
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
|
||||
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
|
||||
)}
|
||||
handleMultithreadAbort();
|
||||
}
|
||||
}
|
||||
|
||||
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers, int layer_nr)
|
||||
@@ -1,10 +1,10 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef LAYERPART_H
|
||||
#define LAYERPART_H
|
||||
#ifndef SLICER_LAYERPART_H
|
||||
#define SLICER_LAYERPART_H
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
#include "slicer.h"
|
||||
#include "commandSocket.h"
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "Slicer.h"
|
||||
#include "../commandSocket.h"
|
||||
|
||||
/*
|
||||
The layer-part creation step is the first step in creating actual useful data for 3D printing.
|
||||
@@ -28,4 +28,4 @@ void layerparts2HTML(SliceDataStorage& mesh, const char* filename, bool all_laye
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//LAYERPART_H
|
||||
#endif//SLICER_LAYERPART_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "multiVolumes.h"
|
||||
#include "MultiVolumes.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef MULTIVOLUMES_H
|
||||
#define MULTIVOLUMES_H
|
||||
#ifndef SLICER_MULTIVOLUMES_H
|
||||
#define SLICER_MULTIVOLUMES_H
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
#include "slicer.h"
|
||||
#include "../sliceDataStorage.h"
|
||||
#include "Slicer.h"
|
||||
|
||||
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
|
||||
namespace cura {
|
||||
@@ -21,4 +21,4 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//MULTIVOLUMES_H
|
||||
#endif//SLICER_MULTIVOLUMES_H
|
||||
@@ -0,0 +1,180 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../utils/gettime.h"
|
||||
#include "../utils/logoutput.h"
|
||||
#include "../textureProcessing/MatCoord.h"
|
||||
#include "../textureProcessing/FaceNormalStorage.h"
|
||||
|
||||
#include "Slicer.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
void Slicer::project2D(unsigned int face_idx, const Point3 p[3], unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, int32_t layer_nr, SlicerSegment& seg)
|
||||
{
|
||||
const Point3& p0 = p[idx_shared];
|
||||
const Point3& p1 = p[idx_first];
|
||||
const Point3& p2 = p[idx_second];
|
||||
|
||||
seg.start.X = interpolate(z, p0.z, p1.z, p0.x, p1.x);
|
||||
seg.start.Y = interpolate(z, p0.z, p1.z, p0.y, p1.y);
|
||||
seg.end .X = interpolate(z, p0.z, p2.z, p0.x, p2.x);
|
||||
seg.end .Y = interpolate(z, p0.z, p2.z, p0.y, p2.y);
|
||||
if (textured_mesh)
|
||||
{
|
||||
MatSegment mat_segment;
|
||||
bool got_texture_coords = textured_mesh->sliceFaceTexture(face_idx, idx_shared, idx_first, idx_second, z, seg.start, seg.end, mat_segment);
|
||||
SlicerLayer& layer = layers[layer_nr];
|
||||
if (got_texture_coords)
|
||||
{
|
||||
if (layer.texture_bump_map)
|
||||
{
|
||||
layer.texture_bump_map->registerTexturedFaceSlice(seg, mat_segment);
|
||||
}
|
||||
if (texture_proximity_processor)
|
||||
{
|
||||
texture_proximity_processor->registerTexturedFaceSlice(seg, mat_segment, layer_nr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, unsigned int slice_layer_count, bool keep_none_closed, bool extensive_stitching, TextureProximityProcessor* texture_proximity_processor)
|
||||
: mesh(mesh)
|
||||
, textured_mesh(dynamic_cast<TexturedMesh*>(mesh))
|
||||
, texture_proximity_processor(texture_proximity_processor)
|
||||
{
|
||||
assert((int) slice_layer_count > 0);
|
||||
|
||||
TimeKeeper slice_timer;
|
||||
|
||||
std::optional<TextureBumpMapProcessor::Settings> bump_map_settings;
|
||||
FaceNormalStorage* face_normal_storage = nullptr;
|
||||
if (mesh->getSettingBoolean("bump_map_enabled"))
|
||||
{
|
||||
bump_map_settings.emplace(mesh);
|
||||
if (mesh->getSettingAsRatio("bump_map_face_angle_correction") != 0.0)
|
||||
{
|
||||
face_normal_storage = new FaceNormalStorage(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
layers.reserve(slice_layer_count);
|
||||
for (uint32_t layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
|
||||
{ // initialize all layers
|
||||
layers.emplace_back(layer_nr, mesh, bump_map_settings, face_normal_storage);
|
||||
assert(&layers.back() == &layers[layer_nr] && "We should just have emplaced the last layer!");
|
||||
layers[layer_nr].z = initial + thickness * layer_nr;
|
||||
}
|
||||
|
||||
bool bump_map_alternate = mesh->getSettingBoolean("bump_map_alternate");
|
||||
int extruder_nr = mesh->getSettingAsIndex("extruder_nr");
|
||||
|
||||
for(unsigned int face_idx = 0; face_idx < mesh->faces.size(); face_idx++)
|
||||
{
|
||||
const MeshFace& face = mesh->faces[face_idx];
|
||||
const MeshVertex& v0 = mesh->vertices[face.vertex_index[0]];
|
||||
const MeshVertex& v1 = mesh->vertices[face.vertex_index[1]];
|
||||
const MeshVertex& v2 = mesh->vertices[face.vertex_index[2]];
|
||||
Point3 p[3] =
|
||||
{ mesh->vertices[face.vertex_index[0]].p
|
||||
, mesh->vertices[face.vertex_index[1]].p
|
||||
, mesh->vertices[face.vertex_index[2]].p };
|
||||
Point3& p0 = p[0];
|
||||
Point3& p1 = p[1];
|
||||
Point3& p2 = p[2];
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ) minZ = p1.z;
|
||||
if (p2.z < minZ) minZ = p2.z;
|
||||
if (p1.z > maxZ) maxZ = p1.z;
|
||||
if (p2.z > maxZ) maxZ = p2.z;
|
||||
int32_t layer_max = (maxZ - initial) / thickness;
|
||||
int32_t layer_min = (minZ - initial + thickness - 1) / thickness; // + thickness - 1 to get the first layer above or at minZ
|
||||
for (int32_t layer_nr = layer_min; layer_nr <= layer_max; layer_nr++)
|
||||
{
|
||||
if (bump_map_alternate && layer_nr % 2 == extruder_nr) // TODO only works for the first two extruders!
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int32_t z = layer_nr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layer_nr < 0) continue;
|
||||
|
||||
SlicerSegment s;
|
||||
s.endVertex = nullptr;
|
||||
s.faceIndex = face_idx;
|
||||
assert(face_idx >= 0);
|
||||
s.addedToPolygon = false;
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[0];
|
||||
if (p1.z == z)
|
||||
{
|
||||
s.endVertex = &v1;
|
||||
}
|
||||
project2D(face_idx, p, 0, 2, 1, z, layer_nr, s);
|
||||
}
|
||||
else if (p0.z > z && p1.z < z && p2.z < z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[2];
|
||||
project2D(face_idx, p, 0, 1, 2, z, layer_nr, s);
|
||||
|
||||
}
|
||||
|
||||
else if (p1.z < z && p0.z >= z && p2.z >= z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[1];
|
||||
if (p2.z == z)
|
||||
{
|
||||
s.endVertex = &v2;
|
||||
}
|
||||
project2D(face_idx, p, 1, 0, 2, z, layer_nr, s);
|
||||
}
|
||||
else if (p1.z > z && p0.z < z && p2.z < z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[0];
|
||||
project2D(face_idx, p, 1, 2, 0, z, layer_nr, s);
|
||||
|
||||
}
|
||||
|
||||
else if (p2.z < z && p1.z >= z && p0.z >= z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[2];
|
||||
if (p0.z == z)
|
||||
{
|
||||
s.endVertex = &v0;
|
||||
}
|
||||
project2D(face_idx, p, 2, 1, 0, z, layer_nr, s);
|
||||
}
|
||||
else if (p2.z > z && p1.z < z && p0.z < z)
|
||||
{
|
||||
s.endOtherFaceIdx = face.connected_face_index[1];
|
||||
project2D(face_idx, p, 2, 0, 1, z, layer_nr, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
layers[layer_nr].face_idx_to_segment_idx.insert(std::make_pair(face_idx, layers[layer_nr].segments.size()));
|
||||
layers[layer_nr].segments.push_back(s);
|
||||
}
|
||||
}
|
||||
log("slice of mesh took %.3f seconds\n",slice_timer.restart());
|
||||
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
|
||||
{
|
||||
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
}
|
||||
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
|
||||
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
|
||||
|
||||
if (face_normal_storage)
|
||||
{
|
||||
delete face_normal_storage;
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,75 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_H
|
||||
#define SLICER_SLICER_H
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "../mesh.h"
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
#include "SlicerSegment.h"
|
||||
#include "ClosePolygonResult.h"
|
||||
#include "SlicerLayer.h"
|
||||
|
||||
#include "../textureProcessing/MatSegment.h"
|
||||
#include "../textureProcessing/TextureProximityProcessor.h"
|
||||
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
The result of the Slicer is a list of polygons without any order or structure.
|
||||
*/
|
||||
namespace cura {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
|
||||
const Mesh* mesh = nullptr; //!< The sliced mesh
|
||||
|
||||
const TexturedMesh* textured_mesh; //!< Pointer to the textured mesh if \ref Slicer::mesh is a TexturedMesh
|
||||
|
||||
TextureProximityProcessor* texture_proximity_processor; //!< Containers for each layer for fast lookup of textures being defined in the proximity of the lookup point
|
||||
|
||||
/*!
|
||||
*
|
||||
* \param texture_proximity_processors (optional) A TextureProximityProcessor for all layers in the mesh
|
||||
*/
|
||||
Slicer(Mesh* mesh, int initial, int thickness, unsigned int slice_layer_count, bool keepNoneClosed, bool extensiveStitching, TextureProximityProcessor* texture_proximity_processors);
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Linear interpolation
|
||||
*
|
||||
* Get the Y of a point with X \p x in the line through (\p x0, \p y0) and (\p x1, \p y1)
|
||||
*
|
||||
* \param p The face vertice locations in the order the vertices are given in the face
|
||||
*/
|
||||
int64_t interpolate(int64_t x, int64_t x0, int64_t x1, int64_t y0, int64_t y1) const
|
||||
{
|
||||
int64_t dx_01 = x1 - x0;
|
||||
int64_t num = (y1 - y0) * (x - x0);
|
||||
num += num > 0 ? dx_01/2 : -dx_01/2; // add in offset to round result
|
||||
int64_t y = y0 + num / dx_01;
|
||||
return y;
|
||||
}
|
||||
|
||||
/*!
|
||||
*
|
||||
* \warning this function requires result.faceIndex to be correctly set already
|
||||
*
|
||||
* \p result where to store the start and end of the sliced segment
|
||||
*/
|
||||
void project2D(unsigned int face_idx, const Point3 p[3], unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, int32_t layer_nr, SlicerSegment& result);
|
||||
|
||||
void dumpSegmentsToHTML(const char* filename);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_SLICER_H
|
||||
@@ -1,22 +1,29 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include <algorithm> // remove_if
|
||||
#include "SlicerLayer.h"
|
||||
#include "../textureProcessing/TextureBumpMapProcessor.h"
|
||||
#include "../utils/SparsePointGridInclusive.h"
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/SparsePointGridInclusive.h"
|
||||
|
||||
#include "slicer.h"
|
||||
#include "multithreadOpenMP.h"
|
||||
|
||||
|
||||
namespace cura {
|
||||
namespace cura
|
||||
{
|
||||
|
||||
int largest_neglected_gap_first_phase = MM2INT(0.01); //!< distance between two line segments regarded as connected
|
||||
int largest_neglected_gap_second_phase = MM2INT(0.02); //!< distance between two line segments regarded as connected
|
||||
int max_stitch1 = MM2INT(10.0); //!< maximal distance stitched between open polylines to form polygons
|
||||
|
||||
|
||||
SlicerLayer::SlicerLayer(unsigned int layer_nr, Mesh* mesh, std::optional<TextureBumpMapProcessor::Settings> bump_map_settings, FaceNormalStorage* face_normal_storage)
|
||||
: layer_nr(layer_nr)
|
||||
{
|
||||
if (bump_map_settings)
|
||||
{
|
||||
TexturedMesh* textured_mesh = dynamic_cast<TexturedMesh*>(mesh);
|
||||
assert(textured_mesh && "we should only have bump map settings when there is a texture");
|
||||
texture_bump_map.emplace(textured_mesh, *bump_map_settings, face_normal_storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SlicerLayer::makeBasicPolygonLoops(const Mesh* mesh, Polygons& open_polylines)
|
||||
{
|
||||
for(unsigned int start_segment_idx = 0; start_segment_idx < segments.size(); start_segment_idx++)
|
||||
@@ -217,7 +224,7 @@ SlicerLayer::findPossibleStitches(
|
||||
// insert the starts of the polylines).
|
||||
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
|
||||
{
|
||||
ConstPolygonRef polyline_0 = open_polylines[polyline_0_idx];
|
||||
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
|
||||
|
||||
if (polyline_0.size() < 1) continue;
|
||||
|
||||
@@ -232,7 +239,7 @@ SlicerLayer::findPossibleStitches(
|
||||
{
|
||||
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
|
||||
{
|
||||
ConstPolygonRef polyline_0 = open_polylines[polyline_0_idx];
|
||||
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
|
||||
|
||||
if (polyline_0.size() < 1) continue;
|
||||
|
||||
@@ -246,7 +253,7 @@ SlicerLayer::findPossibleStitches(
|
||||
// search for nearby end points
|
||||
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
|
||||
{
|
||||
ConstPolygonRef polyline_1 = open_polylines[polyline_1_idx];
|
||||
const PolygonRef polyline_1 = open_polylines[polyline_1_idx];
|
||||
|
||||
if (polyline_1.size() < 1) continue;
|
||||
|
||||
@@ -590,7 +597,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
{
|
||||
if (best_result.pointIdxA == best_result.pointIdxB)
|
||||
{
|
||||
polygons.add(PolygonRef{open_polylines[best_polyline_1_idx]});
|
||||
polygons.add(open_polylines[best_polyline_1_idx]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else if (best_result.AtoB)
|
||||
@@ -605,9 +612,9 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
else
|
||||
{
|
||||
unsigned int n = polygons.size();
|
||||
polygons.add(PolygonRef{open_polylines[best_polyline_1_idx]});
|
||||
polygons.add(open_polylines[best_polyline_1_idx]);
|
||||
for(unsigned int j = best_result.pointIdxB; j != best_result.pointIdxA; j = (j + 1) % polygons[best_result.polygonIdx].size())
|
||||
PolygonRef{polygons[n]}.add(polygons[best_result.polygonIdx][j]);
|
||||
polygons[n].add(polygons[best_result.polygonIdx][j]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
}
|
||||
@@ -616,7 +623,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
if (best_result.pointIdxA == best_result.pointIdxB)
|
||||
{
|
||||
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else if (best_result.AtoB)
|
||||
@@ -625,17 +632,17 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
|
||||
for(unsigned int n = best_result.pointIdxA; n != best_result.pointIdxB; n = (n + 1) % polygons[best_result.polygonIdx].size())
|
||||
poly.add(polygons[best_result.polygonIdx][n]);
|
||||
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(poly[n]);
|
||||
open_polylines[best_polyline_2_idx].add(poly[n]);
|
||||
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int n = best_result.pointIdxB; n != best_result.pointIdxA; n = (n + 1) % polygons[best_result.polygonIdx].size())
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(polygons[best_result.polygonIdx][n]);
|
||||
open_polylines[best_polyline_2_idx].add(polygons[best_result.polygonIdx][n]);
|
||||
for(unsigned int n = open_polylines[best_polyline_1_idx].size() - 1; int(n) >= 0; n--)
|
||||
PolygonRef{open_polylines[best_polyline_2_idx]}.add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
|
||||
open_polylines[best_polyline_1_idx].clear();
|
||||
}
|
||||
}
|
||||
@@ -775,6 +782,11 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
|
||||
auto it = std::remove_if(polygons.begin(), polygons.end(), [snapDistance](PolygonRef poly) { return poly.shorterThan(snapDistance); });
|
||||
polygons.erase(it, polygons.end());
|
||||
|
||||
if (texture_bump_map)
|
||||
{
|
||||
texture_bump_map->processBumpMap(polygons, layer_nr);
|
||||
}
|
||||
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
polygons.simplify();
|
||||
|
||||
@@ -787,118 +799,4 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keep_none_closed, bool extensive_stitching)
|
||||
: mesh(mesh)
|
||||
{
|
||||
assert(slice_layer_count > 0);
|
||||
|
||||
TimeKeeper slice_timer;
|
||||
|
||||
layers.resize(slice_layer_count);
|
||||
|
||||
|
||||
for(int32_t layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
|
||||
{
|
||||
layers[layer_nr].z = initial + thickness * layer_nr;
|
||||
}
|
||||
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < mesh->faces.size(); mesh_idx++)
|
||||
{
|
||||
const MeshFace& face = mesh->faces[mesh_idx];
|
||||
const MeshVertex& v0 = mesh->vertices[face.vertex_index[0]];
|
||||
const MeshVertex& v1 = mesh->vertices[face.vertex_index[1]];
|
||||
const MeshVertex& v2 = mesh->vertices[face.vertex_index[2]];
|
||||
Point3 p0 = v0.p;
|
||||
Point3 p1 = v1.p;
|
||||
Point3 p2 = v2.p;
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ) minZ = p1.z;
|
||||
if (p2.z < minZ) minZ = p2.z;
|
||||
if (p1.z > maxZ) maxZ = p1.z;
|
||||
if (p2.z > maxZ) maxZ = p2.z;
|
||||
int32_t layer_max = (maxZ - initial) / thickness;
|
||||
for(int32_t layer_nr = (minZ - initial) / thickness; layer_nr <= layer_max; layer_nr++)
|
||||
{
|
||||
int32_t z = layer_nr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layer_nr < 0) continue;
|
||||
|
||||
SlicerSegment s;
|
||||
s.endVertex = nullptr;
|
||||
int end_edge_idx = -1;
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
{
|
||||
s = project2D(p0, p2, p1, z);
|
||||
end_edge_idx = 0;
|
||||
if (p1.z == z)
|
||||
{
|
||||
s.endVertex = &v1;
|
||||
}
|
||||
}
|
||||
else if (p0.z > z && p1.z < z && p2.z < z)
|
||||
{
|
||||
s = project2D(p0, p1, p2, z);
|
||||
end_edge_idx = 2;
|
||||
|
||||
}
|
||||
|
||||
else if (p1.z < z && p0.z >= z && p2.z >= z)
|
||||
{
|
||||
s = project2D(p1, p0, p2, z);
|
||||
end_edge_idx = 1;
|
||||
if (p2.z == z)
|
||||
{
|
||||
s.endVertex = &v2;
|
||||
}
|
||||
}
|
||||
else if (p1.z > z && p0.z < z && p2.z < z)
|
||||
{
|
||||
s = project2D(p1, p2, p0, z);
|
||||
end_edge_idx = 0;
|
||||
|
||||
}
|
||||
|
||||
else if (p2.z < z && p1.z >= z && p0.z >= z)
|
||||
{
|
||||
s = project2D(p2, p1, p0, z);
|
||||
end_edge_idx = 2;
|
||||
if (p0.z == z)
|
||||
{
|
||||
s.endVertex = &v0;
|
||||
}
|
||||
}
|
||||
else if (p2.z > z && p1.z < z && p0.z < z)
|
||||
{
|
||||
s = project2D(p2, p0, p1, z);
|
||||
end_edge_idx = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
layers[layer_nr].face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layers[layer_nr].segments.size()));
|
||||
s.faceIndex = mesh_idx;
|
||||
s.endOtherFaceIdx = face.connected_face_index[end_edge_idx];
|
||||
s.addedToPolygon = false;
|
||||
layers[layer_nr].segments.push_back(s);
|
||||
}
|
||||
}
|
||||
log("slice of mesh took %.3f seconds\n",slice_timer.restart());
|
||||
|
||||
auto& layers_ref = layers;
|
||||
#pragma omp parallel for default(none) shared(mesh,layers_ref) firstprivate(keep_none_closed, extensive_stitching)
|
||||
for(unsigned int layer_nr=0; layer_nr<layers_ref.size(); layer_nr++)
|
||||
{ MULTITHREAD_FOR_CATCH_EXCEPTION(
|
||||
layers_ref[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
|
||||
)}
|
||||
handleMultithreadAbort();
|
||||
|
||||
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
|
||||
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
} // namespace cura
|
||||
@@ -1,58 +1,46 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_H
|
||||
#define SLICER_H
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_LAYER_H
|
||||
#define SLICER_SLICER_LAYER_H
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "mesh.h"
|
||||
#include "utils/polygon.h"
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
The result of the Slicer is a list of polygons without any order or structure.
|
||||
*/
|
||||
namespace cura {
|
||||
#include "../utils/optional.h"
|
||||
#include "../mesh.h"
|
||||
#include "../utils/intpoint.h"
|
||||
#include "../utils/polygon.h"
|
||||
|
||||
class SlicerSegment
|
||||
#include "SlicerSegment.h"
|
||||
#include "GapCloserResult.h"
|
||||
#include "ClosePolygonResult.h"
|
||||
|
||||
#include "../textureProcessing/MatSegment.h"
|
||||
#include "../textureProcessing/TextureBumpMapProcessor.h"
|
||||
#include "../textureProcessing/FaceNormalStorage.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
public:
|
||||
Point start, end;
|
||||
int faceIndex = -1;
|
||||
// The index of the other face connected via the edge that created end
|
||||
int endOtherFaceIdx = -1;
|
||||
// If end corresponds to a vertex of the mesh, then this is populated
|
||||
// with the vertex that it ended on.
|
||||
const MeshVertex *endVertex = nullptr;
|
||||
bool addedToPolygon = false;
|
||||
};
|
||||
|
||||
class ClosePolygonResult
|
||||
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
|
||||
//The line on which the point lays is between pointIdx-1 and pointIdx
|
||||
public:
|
||||
Point intersectionPoint;
|
||||
int polygonIdx = -1;
|
||||
unsigned int pointIdx = -1;
|
||||
};
|
||||
class GapCloserResult
|
||||
{
|
||||
public:
|
||||
int64_t len = -1;
|
||||
int polygonIdx = -1;
|
||||
unsigned int pointIdxA = -1;
|
||||
unsigned int pointIdxB = -1;
|
||||
bool AtoB = false;
|
||||
};
|
||||
|
||||
class SlicerLayer
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* \param mesh For which mesh this layer is sliced
|
||||
* \param bump_map_settings The settings with which to create a TextureBumpMapProcessor - if provided
|
||||
* \param face_normal_storage The face normal statistics to be used in the \p bump_map_settings - if provided
|
||||
*/
|
||||
SlicerLayer(unsigned int layer_nr, Mesh* mesh, std::optional<TextureBumpMapProcessor::Settings> bump_map_settings, FaceNormalStorage* face_normal_storage);
|
||||
|
||||
std::vector<SlicerSegment> segments;
|
||||
std::unordered_map<int, int> face_idx_to_segment_idx; // topology
|
||||
|
||||
int z = -1;
|
||||
unsigned int layer_nr;
|
||||
|
||||
Polygons polygons;
|
||||
Polygons openPolylines;
|
||||
|
||||
std::optional<TextureBumpMapProcessor> texture_bump_map; //!< the bump map to apply to the outlines - if any
|
||||
|
||||
/*!
|
||||
* Connect the segments into polygons for this layer of this \p mesh
|
||||
*
|
||||
@@ -483,44 +471,6 @@ private:
|
||||
bool allow_reverse);
|
||||
};
|
||||
|
||||
class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
} // namespace cura
|
||||
|
||||
const Mesh* mesh = nullptr; //!< The sliced mesh
|
||||
|
||||
Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keepNoneClosed, bool extensiveStitching);
|
||||
|
||||
/*!
|
||||
* Linear interpolation
|
||||
*
|
||||
* Get the Y of a point with X \p x in the line through (\p x0, \p y0) and (\p x1, \p y1)
|
||||
*/
|
||||
int64_t interpolate(int64_t x, int64_t x0, int64_t x1, int64_t y0, int64_t y1) const
|
||||
{
|
||||
int64_t dx_01 = x1 - x0;
|
||||
int64_t num = (y1 - y0) * (x - x0);
|
||||
num += num > 0 ? dx_01/2 : -dx_01/2; // add in offset to round result
|
||||
int64_t y = y0 + num / dx_01;
|
||||
return y;
|
||||
}
|
||||
|
||||
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
|
||||
{
|
||||
SlicerSegment seg;
|
||||
|
||||
seg.start.X = interpolate(z, p0.z, p1.z, p0.x, p1.x);
|
||||
seg.start.Y = interpolate(z, p0.z, p1.z, p0.y, p1.y);
|
||||
seg.end .X = interpolate(z, p0.z, p2.z, p0.x, p2.x);
|
||||
seg.end .Y = interpolate(z, p0.z, p2.z, p0.y, p2.y);
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
void dumpSegmentsToHTML(const char* filename);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//SLICER_H
|
||||
#endif // SLICER_SLICER_LAYER_H
|
||||
@@ -0,0 +1,59 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_SLICER_SEGMENT_H
|
||||
#define SLICER_SLICER_SEGMENT_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
|
||||
#include "../mesh.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class SlicerSegment
|
||||
{
|
||||
public:
|
||||
Point start, end;
|
||||
int faceIndex = -1;
|
||||
// The index of the other face connected via the edge that created end
|
||||
int endOtherFaceIdx = -1;
|
||||
// If end corresponds to a vertex of the mesh, then this is populated
|
||||
// with the vertex that it ended on.
|
||||
const MeshVertex *endVertex = nullptr;
|
||||
bool addedToPolygon = false;
|
||||
|
||||
SlicerSegment() //!< non-initializing constructor
|
||||
{}
|
||||
SlicerSegment(Point start, Point end) //!< partially initializing constructor
|
||||
: start(start)
|
||||
, end(end)
|
||||
{}
|
||||
/*!
|
||||
* equivalence testing irrespective of start/end order
|
||||
*/
|
||||
bool operator==(const SlicerSegment& b) const
|
||||
{
|
||||
return (start == b.start && end == b.end) || (start == b.end && end == b.start);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
namespace std
|
||||
{
|
||||
/*!
|
||||
* hash function irrespective of start/end order
|
||||
*/
|
||||
template<> struct hash<cura::SlicerSegment>
|
||||
{
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(cura::SlicerSegment const& s) const
|
||||
{
|
||||
return std::hash<cura::Point>()(cura::operator+(s.start, s.end));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
|
||||
#endif // SLICER_SLICER_SEGMENT_H
|
||||
+48
-54
@@ -90,7 +90,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
storage.support.supportLayers.resize(layer_count);
|
||||
}
|
||||
|
||||
// generate support areas
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
@@ -101,33 +100,25 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
|
||||
std::vector<Polygons> supportAreas;
|
||||
supportAreas.resize(layer_count, Polygons());
|
||||
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
|
||||
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
|
||||
|
||||
if (mesh.getSettingBoolean("support_interface_enable"))
|
||||
{
|
||||
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
|
||||
generateSupportInterface(storage, mesh, supportAreas, layer_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
{
|
||||
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
||||
{
|
||||
Polygons& support_areas = storage.support.supportLayers[layer_idx].supportAreas;
|
||||
support_areas = support_areas.unionPolygons();
|
||||
}
|
||||
|
||||
// handle support interface
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
{
|
||||
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
||||
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mesh.getSettingBoolean("support_interface_enable"))
|
||||
{
|
||||
generateSupportInterface(storage, mesh, layer_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -351,14 +342,14 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
|
||||
}
|
||||
|
||||
//Enforce top Z distance.
|
||||
if (layerZdistanceTop > 1)
|
||||
if (layerZdistanceTop > 0)
|
||||
{
|
||||
// this is performed after the main support generation loop above, because it affects the joining of polygons
|
||||
// if this would be performed in the main loop then some support would not have been generated under the overhangs and consequently no support is generated for that,
|
||||
// meaning almost no support would be generated in some cases which definitely need support.
|
||||
for (size_t layer_idx = 0; layer_idx < storage.support.supportLayers.size() && layer_idx < support_layer_count - (layerZdistanceTop - 1); layer_idx++)
|
||||
for (size_t layer_idx = 0; layer_idx < storage.support.supportLayers.size() && layer_idx < support_layer_count - layerZdistanceTop; layer_idx++)
|
||||
{
|
||||
supportAreas[layer_idx] = supportAreas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layerZdistanceTop - 1, false));
|
||||
supportAreas[layer_idx] = supportAreas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layerZdistanceTop, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +499,7 @@ void AreaSupport::handleTowers(
|
||||
{
|
||||
supportLayer_this = supportLayer_this.unionPolygons(tower_roof);
|
||||
|
||||
if (PolygonRef{tower_roof[0]}.area() < supportTowerDiameter * supportTowerDiameter)
|
||||
if (tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
|
||||
{
|
||||
tower_roof = tower_roof.offset(towerRoofExpansionDistance);
|
||||
}
|
||||
@@ -568,7 +559,7 @@ void AreaSupport::handleWallStruts(
|
||||
}
|
||||
|
||||
|
||||
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_count)
|
||||
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count)
|
||||
{
|
||||
const unsigned int roof_layer_count = round_divide(mesh.getSettingInMicrons("support_roof_height"), storage.getSettingInMicrons("layer_height"));
|
||||
const unsigned int bottom_layer_count = round_divide(mesh.getSettingInMicrons("support_bottom_height"), storage.getSettingInMicrons("layer_height"));
|
||||
@@ -585,41 +576,44 @@ void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const Slic
|
||||
|
||||
const unsigned int top_layer_idx_above = layer_idx + roof_layer_count + z_distance_top;
|
||||
const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom));
|
||||
if (top_layer_idx_above >= supportLayers.size())
|
||||
if (top_layer_idx_above < supportLayers.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Polygons roofs;
|
||||
if (roof_layer_count > 0)
|
||||
{
|
||||
Polygons model;
|
||||
const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count);
|
||||
const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans));
|
||||
for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip)
|
||||
Polygons roofs;
|
||||
if (roof_layer_count > 0)
|
||||
{
|
||||
const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines();
|
||||
model = model.unionPolygons(outlines_above);
|
||||
Polygons model;
|
||||
const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count);
|
||||
const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans));
|
||||
for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip)
|
||||
{
|
||||
const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines();
|
||||
model = model.unionPolygons(outlines_above);
|
||||
}
|
||||
roofs = support_areas[layer_idx].intersection(model);
|
||||
}
|
||||
roofs = layer.supportAreas.intersection(model);
|
||||
}
|
||||
Polygons bottoms;
|
||||
if (bottom_layer_count > 0)
|
||||
{
|
||||
Polygons model;
|
||||
const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count);
|
||||
const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans));
|
||||
for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip)
|
||||
Polygons bottoms;
|
||||
if (bottom_layer_count > 0)
|
||||
{
|
||||
const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines();
|
||||
model = model.unionPolygons(outlines_below);
|
||||
Polygons model;
|
||||
const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count);
|
||||
const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans));
|
||||
for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip)
|
||||
{
|
||||
const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines();
|
||||
model = model.unionPolygons(outlines_below);
|
||||
}
|
||||
bottoms = support_areas[layer_idx].intersection(model);
|
||||
}
|
||||
bottoms = layer.supportAreas.intersection(model);
|
||||
// expand skin a bit so that we're sure it's not too thin to be printed.
|
||||
Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(support_areas[layer_idx]);
|
||||
skin.removeSmallAreas(1.0);
|
||||
layer.skin.add(skin);
|
||||
layer.supportAreas.add(support_areas[layer_idx].difference(layer.skin));
|
||||
}
|
||||
else
|
||||
{
|
||||
layer.skin.add(support_areas[layer_idx]);
|
||||
}
|
||||
// expand skin a bit so that we're sure it's not too thin to be printed.
|
||||
Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(layer.supportAreas);
|
||||
skin.removeSmallAreas(1.0);
|
||||
layer.skin.add(skin);
|
||||
layer.supportAreas = layer.supportAreas.difference(layer.skin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -36,9 +36,10 @@ private:
|
||||
*
|
||||
* \param storage Output storage: support area + support skin area output
|
||||
* \param mesh The mesh to generate support skins for.
|
||||
* \param support_areas The basic support areas for the current mesh
|
||||
* \param layer_count The number of layers in this mesh group.
|
||||
*/
|
||||
static void generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_count);
|
||||
static void generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count);
|
||||
|
||||
/*!
|
||||
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include <limits> // numeric_limits
|
||||
#include <cmath> // isnan
|
||||
|
||||
#include "FaceNormalStorage.h"
|
||||
|
||||
#include <math.h> // debug
|
||||
|
||||
#define NORMAL_LENGTH 10000
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
FaceNormalStorage::FaceNormalStorage(Mesh* mesh)
|
||||
{
|
||||
face_normal_vertical_component.reserve(mesh->faces.size());
|
||||
for (MeshFace& face : mesh->faces)
|
||||
{
|
||||
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
|
||||
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
|
||||
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
|
||||
face_normal_vertical_component.emplace_back(computeFaceTanAngle(p0, p1, p2));
|
||||
}
|
||||
}
|
||||
|
||||
float FaceNormalStorage::computeFaceTanAngle(const Point3 p0, const Point3 p1, const Point3 p2) const
|
||||
{
|
||||
Point3 v01 = p1 - p0;
|
||||
Point3 v01_n = v01.normal(NORMAL_LENGTH);
|
||||
Point3 v02 = p2 - p0;
|
||||
Point3 v02_n = v02.normal(NORMAL_LENGTH);
|
||||
Point3 normal_dir = v01_n.cross(v02_n);
|
||||
coord_t z_component = normal_dir.z;
|
||||
coord_t xy_component = vSize(Point(normal_dir.x, normal_dir.y));
|
||||
if (xy_component > -2 && xy_component < 2)
|
||||
{
|
||||
if (z_component > 0)
|
||||
{
|
||||
return std::numeric_limits<float>::infinity();
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1 * std::numeric_limits<float>::infinity();
|
||||
}
|
||||
}
|
||||
float ret = (float) z_component / (float) xy_component;
|
||||
assert(!std::isnan(ret));
|
||||
assert(!std::isnan(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
float FaceNormalStorage::getFaceTanAngle(unsigned int face_idx)
|
||||
{
|
||||
return face_normal_vertical_component[face_idx];
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,42 @@
|
||||
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_FACE_NORMAL_STORAGE_H
|
||||
#define TEXTURE_PROCESSING_FACE_NORMAL_STORAGE_H
|
||||
|
||||
#include "../mesh.h"
|
||||
#include "../utils/NoCopy.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* helper class for storing mesh face data to be used by each TextureBumpMapProcessor of one mesh
|
||||
*/
|
||||
class FaceNormalStorage : NoCopy
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructor to compute the tan angle for all faces in the model.
|
||||
*/
|
||||
FaceNormalStorage(Mesh* mesh);
|
||||
|
||||
/*!
|
||||
* Get the horizontal component of the face normal
|
||||
*
|
||||
* returns a negative amount for faces angling downward
|
||||
* (TODO verify above sentence)
|
||||
* \return the ratio between the vertical and the horizontal aspect of the normal of the face with index \p face_index (in the list of faes in the \ref Mesh)
|
||||
*/
|
||||
float getFaceTanAngle(unsigned int face_idx);
|
||||
protected:
|
||||
|
||||
/*!
|
||||
* compute the tan angle of one face
|
||||
* \p p0, \p p1 and \p p2 should be in CCW order
|
||||
*/
|
||||
float computeFaceTanAngle(const Point3 p0, const Point3 p1, const Point3 p2) const;
|
||||
std::vector<float> face_normal_vertical_component; //!< for each face the horizontal component of the normal angle
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_FACE_NORMAL_STORAGE_H
|
||||
@@ -0,0 +1,44 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_MAT_COORD_H
|
||||
#define TEXTURE_PROCESSING_MAT_COORD_H
|
||||
|
||||
#include "../utils/FPoint.h"
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Coordinates in a specific texture bitmap
|
||||
*/
|
||||
struct MatCoord
|
||||
{
|
||||
FPoint coords;
|
||||
const Material* mat; //!< Material id
|
||||
MatCoord() //!< non-initializing constructor
|
||||
{}
|
||||
MatCoord(FPoint coords, const Material& mat) //!< constructor
|
||||
: coords(coords)
|
||||
, mat(&mat)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Get the color of the material to which this coordinate is pointing
|
||||
*/
|
||||
float getColor(ColourUsage color) const
|
||||
{
|
||||
if (mat)
|
||||
{
|
||||
return mat->getColor(coords.x, coords.y, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_MAT_COORD_H
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_MAT_SEGMENT_H
|
||||
#define TEXTURE_PROCESSING_MAT_SEGMENT_H
|
||||
|
||||
#include "MatCoord.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Coordinates in a specific texture bitmap
|
||||
*/
|
||||
struct MatSegment
|
||||
{
|
||||
MatCoord start;
|
||||
MatCoord end;
|
||||
MatSegment() //!< non-initializing constructor
|
||||
{}
|
||||
MatSegment(MatCoord start, MatCoord end)
|
||||
: start(start)
|
||||
, end(end)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_MAT_SEGMENT_H
|
||||
@@ -0,0 +1,178 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include <limits> // numeric limits
|
||||
#include <algorithm> // min max
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
#define STBI_FAILURE_USERMSG // enable user friendly bug messages for STB lib
|
||||
#define STB_IMAGE_IMPLEMENTATION // needed in order to enable the implementation of libs/std_image.h
|
||||
#include "stb/stb_image.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
/*!
|
||||
* custom destructor for the data to be used by the shared_pointer
|
||||
*/
|
||||
struct ArrayDeleter
|
||||
{
|
||||
void operator ()(unsigned char* p)
|
||||
{
|
||||
stbi_image_free(p);
|
||||
}
|
||||
};
|
||||
|
||||
Material::Material()
|
||||
: data(nullptr, ArrayDeleter())
|
||||
, width(0)
|
||||
, height(0)
|
||||
, depth(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Material::~Material()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Material::loadImage(const char* filename)
|
||||
{
|
||||
int w, h, d;
|
||||
// in RGBA order
|
||||
int desired_channel_count = 0; // keep original amount of channels
|
||||
unsigned char* data = stbi_load(filename, &w, &h, &d, desired_channel_count);
|
||||
if (data)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
depth = d;
|
||||
this->data = std::shared_ptr<unsigned char>(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* reason = "[unknown reason]";
|
||||
if (stbi_failure_reason())
|
||||
{
|
||||
reason = stbi_failure_reason();
|
||||
}
|
||||
logError("Cannot load image %s: '%s'.\n", filename, reason);
|
||||
std::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
float Material::getColor(float x, float y, ColourUsage color) const
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
assert(x >= 0.0f && x <= 1.0f);
|
||||
assert(y >= 0.0f && y <= 1.0f);
|
||||
switch (color)
|
||||
{
|
||||
case ColourUsage::RED:
|
||||
case ColourUsage::GREEN:
|
||||
case ColourUsage::BLUE:
|
||||
case ColourUsage::ALPHA:
|
||||
{
|
||||
assert((int)color >= 0 && (unsigned int)color < depth && "Z out of bounds!");
|
||||
return getColorData(x, y, (unsigned int) color);
|
||||
}
|
||||
case ColourUsage::GREY:
|
||||
default:
|
||||
{
|
||||
float r = getColorData(x, y, (unsigned int) ColourUsage::RED);
|
||||
float g = getColorData(x, y, (unsigned int) ColourUsage::GREEN);
|
||||
float b = getColorData(x, y, (unsigned int) ColourUsage::BLUE);
|
||||
return (r + g + b) / 3.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Material::getColorData(float x, float y, unsigned int z) const
|
||||
{
|
||||
unsigned int x_idx = (unsigned int) (x * (width - 1) + 0.5);
|
||||
assert(x_idx >= 0 && x_idx < width && "requested X is out of bounds!");
|
||||
unsigned int y_idx = (unsigned int) (y * (height - 1) + 0.5);
|
||||
assert(y_idx >= 0 && y_idx < height && "requested Y is out of bounds!");
|
||||
|
||||
unsigned char col = data.get()[((height - y_idx - 1) * width + x_idx) * depth + z];
|
||||
return (float) col / std::numeric_limits<unsigned char>::max();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Material::debugOutput(bool dw) const
|
||||
{
|
||||
std::cerr << "\nImage size: " << width << " x " << height << " (" << depth << "channels)\n";
|
||||
std::cerr << '+';
|
||||
for (unsigned int i = 0; i < width; i++)
|
||||
{
|
||||
std::cerr << ((dw)? "--" : "-");
|
||||
}
|
||||
std::cerr << "+\n";
|
||||
for (unsigned int y = 0; y < height; y++)
|
||||
{
|
||||
std::cerr << "|";
|
||||
for (unsigned int x = 0; x < width; x++)
|
||||
{
|
||||
int val = (data.get()[((height - y) * width + x) * depth] * 10 / 256);
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case 0:
|
||||
std::cerr << ((dw)? " " : " ");
|
||||
break;
|
||||
case 1:
|
||||
std::cerr << ((dw)? ".." : ".");
|
||||
break;
|
||||
case 2:
|
||||
std::cerr << ((dw)? ",," : ",");
|
||||
break;
|
||||
case 3:
|
||||
std::cerr << ((dw)? "::" : ":");
|
||||
break;
|
||||
case 4:
|
||||
std::cerr << ((dw)? ";;" : ";");
|
||||
break;
|
||||
case 5:
|
||||
std::cerr << ((dw)? "++" : "+");
|
||||
break;
|
||||
case 6:
|
||||
std::cerr << ((dw)? "░░" : "░");
|
||||
break;
|
||||
case 7:
|
||||
std::cerr << ((dw)? "▒▒" : "▒");
|
||||
break;
|
||||
case 8:
|
||||
std::cerr << ((dw)? "▓▓" : "▓");
|
||||
break;
|
||||
default:
|
||||
if (val > 8)
|
||||
{
|
||||
std::cerr << ((dw)? "██" : "█");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << ((dw)? " " : " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cerr << "|\n";
|
||||
}
|
||||
std::cerr << '+';
|
||||
for (unsigned int i = 0; i < width; i++)
|
||||
{
|
||||
std::cerr << ((dw)? "--" : "-");
|
||||
}
|
||||
std::cerr << "+\n";
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,71 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_MATERIAL_H
|
||||
#define TEXTURE_PROCESSING_MATERIAL_H
|
||||
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include "../settings/settings.h" // ColourUsage
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* The material used in a texture.
|
||||
*
|
||||
* This class just holds the image data and has some nice utility functions.
|
||||
*/
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* non-initializing constructor
|
||||
*/
|
||||
Material();
|
||||
|
||||
/*!
|
||||
* Destructor
|
||||
*
|
||||
* deletes the image data
|
||||
*/
|
||||
~Material();
|
||||
|
||||
/*!
|
||||
* Load an image from file.
|
||||
*
|
||||
* Crash if this doesn't work. (unsupported file type, IO exception, etc.)
|
||||
*/
|
||||
void loadImage(const char* filename);
|
||||
|
||||
/*!
|
||||
* get the color value at a particular place in the image
|
||||
*
|
||||
* \param x place in the horizontal direction left to right (value between zero and one)
|
||||
* \param y place in the vertical direction top to bottom (value between zero and one)
|
||||
* \param color The color channel to check
|
||||
* \return a value between zero and one
|
||||
*/
|
||||
float getColor(float x, float y, ColourUsage color) const;
|
||||
|
||||
/*!
|
||||
* print out something which looks like the picture through std::cerr
|
||||
* \param double_width Whether to double each character being written, so that the width is visually similar to the height of each pixel.
|
||||
*/
|
||||
void debugOutput(bool double_width = true) const;
|
||||
protected:
|
||||
|
||||
std::shared_ptr<unsigned char> data; //!< pixel data in rgb-row-first (or bgr-row first ?)
|
||||
unsigned int width, height, depth; //!< image dimensions
|
||||
|
||||
/*!
|
||||
* Get a color value from the data
|
||||
* \param x place in the horizontal direction left to right (value between zero and one)
|
||||
* \param y place in the vertical direction top to bottom (value between zero and one)
|
||||
* \return the color data (0-256)
|
||||
*/
|
||||
float getColorData(float x, float y, unsigned int z) const;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_MATERIAL_H
|
||||
@@ -0,0 +1,42 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "MaterialBase.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
Material* MaterialBase::add(std::string name)
|
||||
{
|
||||
name_to_mat_idx[name] = materials.size();
|
||||
materials.emplace_back();
|
||||
return &materials.back();
|
||||
}
|
||||
|
||||
const Material* MaterialBase::getMat(unsigned int id) const
|
||||
{
|
||||
if (id < materials.size())
|
||||
{
|
||||
return &materials[id];
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MaterialBase::getMatId(std::string name) const
|
||||
{
|
||||
auto it = name_to_mat_idx.find(name);
|
||||
if (it == name_to_mat_idx.end())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,27 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_MATERIAL_BASE_H
|
||||
#define TEXTURE_PROCESSING_MATERIAL_BASE_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class MaterialBase
|
||||
{
|
||||
public:
|
||||
int getMatId(std::string name) const;
|
||||
Material* add(std::string name);
|
||||
const Material* getMat(unsigned int id) const;
|
||||
protected:
|
||||
std::unordered_map<std::string, int> name_to_mat_idx;
|
||||
std::vector<Material> materials;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_MATERIAL_BASE_H
|
||||
@@ -0,0 +1,289 @@
|
||||
#include "TextureBumpMapProcessor.h"
|
||||
|
||||
#include <algorithm> // swap
|
||||
#include <cmath> // fabs
|
||||
|
||||
#include "../utils/optional.h"
|
||||
#include "../utils/linearAlg2D.h"
|
||||
#include "../slicer/SlicerSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
#define SLICE_SEGMENT_SNAP_GAP 20
|
||||
|
||||
TextureBumpMapProcessor::TextureBumpMapProcessor(TexturedMesh* mesh, const TextureBumpMapProcessor::Settings settings, FaceNormalStorage* face_normal_storage)
|
||||
: mesh(mesh)
|
||||
, settings(settings)
|
||||
, face_normal_storage(face_normal_storage)
|
||||
, loc_to_slice(SLICE_SEGMENT_SNAP_GAP)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void TextureBumpMapProcessor::registerTexturedFaceSlice(SlicerSegment face_segment, MatSegment texture_segment)
|
||||
{
|
||||
assert(face_segment.faceIndex >= 0);
|
||||
TexturedFaceSlice slice{face_segment, texture_segment};
|
||||
loc_to_slice.insert(face_segment.start, slice);
|
||||
loc_to_slice.insert(face_segment.end, slice);
|
||||
}
|
||||
|
||||
std::optional<TextureBumpMapProcessor::TexturedFaceSlice> TextureBumpMapProcessor::getTexturedFaceSlice(Point p0, Point p1)
|
||||
{
|
||||
std::vector<TexturedFaceSlice> nearby_slices = loc_to_slice.getNearby(p0, SLICE_SEGMENT_SNAP_GAP);
|
||||
std::optional<TexturedFaceSlice> best;
|
||||
coord_t best_dist_score = std::numeric_limits<coord_t>::max();
|
||||
|
||||
for (TexturedFaceSlice& slice : nearby_slices)
|
||||
{
|
||||
coord_t dist_score = std::min(
|
||||
vSize2(slice.face_segment.start - p0) + vSize2(slice.face_segment.end - p1)
|
||||
, vSize2(slice.face_segment.end - p0) + vSize2(slice.face_segment.start - p1)
|
||||
);
|
||||
if (dist_score < best_dist_score)
|
||||
{
|
||||
best = slice;
|
||||
best_dist_score = dist_score;
|
||||
}
|
||||
}
|
||||
if (best_dist_score > SLICE_SEGMENT_SNAP_GAP * SLICE_SEGMENT_SNAP_GAP * 4) // TODO: this condition doesn't follow exactly from using SLICE_SEGMENT_SNAP_GAP and the quadratic dist score
|
||||
{
|
||||
return std::optional<TextureBumpMapProcessor::TexturedFaceSlice>();
|
||||
}
|
||||
if (vSize2(best->face_segment.start - p0) > vSize2(best->face_segment.start - p1))
|
||||
{
|
||||
std::swap(best->face_segment.start, best->face_segment.end);
|
||||
}
|
||||
assert(best->face_segment.faceIndex >= 0);
|
||||
return best;
|
||||
}
|
||||
|
||||
coord_t TextureBumpMapProcessor::getOffset(const float color, const int face_idx)
|
||||
{
|
||||
coord_t extra_offset = 0;
|
||||
if (face_normal_storage)
|
||||
{
|
||||
assert(face_idx >= 0 && "we must know for which face we are getting the color");
|
||||
float tan_angle = face_normal_storage->getFaceTanAngle(face_idx);
|
||||
float abs_tan_angle = std::fabs(tan_angle);
|
||||
abs_tan_angle = std::min(abs_tan_angle, settings.max_tan_correction_angle);
|
||||
extra_offset = settings.face_angle_correction * (color - 0.5) * abs_tan_angle * settings.layer_height;
|
||||
// (color - 0.5) so that the color causes either an outset or an inset which is
|
||||
// within the range [-0.5, 0.5] so that when at max it will coincide with the min on the previous layer:
|
||||
//
|
||||
// for a black mesh
|
||||
// bridged gap = 4 applied offset = 2 and -2
|
||||
// ^^^^ ^^
|
||||
// ____ ______^^
|
||||
// :_______ :_____
|
||||
// : : : will become : : :
|
||||
}
|
||||
return color * (settings.amplitude * 2) - settings.amplitude + settings.offset + extra_offset;
|
||||
}
|
||||
|
||||
coord_t TextureBumpMapProcessor::getCornerOffset(std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& textured_face_slice, std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& next_textured_face_slice)
|
||||
{
|
||||
coord_t offset0 = 0; // where no texture is present, no offset is applied
|
||||
coord_t offset1 = 0;
|
||||
if (textured_face_slice)
|
||||
{
|
||||
const float color0 = textured_face_slice->mat_segment.end.getColor(settings.color_usage);
|
||||
const int face_0_idx = textured_face_slice->face_segment.faceIndex;
|
||||
offset0 = getOffset(color0, face_0_idx);
|
||||
}
|
||||
if (next_textured_face_slice)
|
||||
{
|
||||
const float color1 = next_textured_face_slice->mat_segment.start.getColor(settings.color_usage);
|
||||
const int face_1_idx = next_textured_face_slice->face_segment.faceIndex;
|
||||
offset1 = getOffset(color1, face_1_idx);
|
||||
}
|
||||
coord_t offset = (offset0 + offset1) / 2;
|
||||
return offset;
|
||||
}
|
||||
|
||||
coord_t TextureBumpMapProcessor::getCornerDisregard(Point p0, Point p1, Point p2, std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& textured_face_slice, std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& next_textured_face_slice)
|
||||
{
|
||||
coord_t offset = getCornerOffset(textured_face_slice, next_textured_face_slice);
|
||||
if ((LinearAlg2D::pointIsLeftOfLine(p1, p0, p2) < 0) == (offset > 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Point v01 = p1 - p0;
|
||||
Point v12 = p2 - p1;
|
||||
assert(p0 != p1 && "Code below depends on v01 not being of zer o size");
|
||||
assert(p1 != p2 && "This function assumes the three points are different");
|
||||
Point n01 = normal(turn90CCW(v01), -1000);
|
||||
Point n12 = normal(turn90CCW(v12), -1000);
|
||||
Point corner_normal = n01 + n12;
|
||||
coord_t corner_normal_size2 = vSize2(corner_normal);
|
||||
coord_t normal_aspect = dot(corner_normal, v01) / vSize(v01); // The aspect of the corner normal along v01 (might be negative)
|
||||
coord_t dist_aspect = sqrt(std::max((coord_t)1, corner_normal_size2 - normal_aspect * normal_aspect)); // The distance of the end of the normal vector to v01 or v12
|
||||
// ^ due to rounding errors 'corner_normal_size2 - normal_aspect^2' may be smaller than zero; because of division on line below should be at least 1
|
||||
coord_t disregard = std::abs(offset * normal_aspect) / dist_aspect;
|
||||
assert(disregard >= 0);
|
||||
return disregard;
|
||||
}
|
||||
|
||||
|
||||
void TextureBumpMapProcessor::processSegmentBumpMap(unsigned int layer_nr, const SlicerSegment& slicer_segment, const MatSegment& mat, const Point p0, const Point p1, coord_t& dist_left_over, coord_t corner_disregard_p1, PolygonRef result)
|
||||
{
|
||||
assert(mat.start.mat == mat.end.mat && "texture across face must be from one material!");
|
||||
|
||||
Point p0p1 = p1 - p0;
|
||||
int64_t p0p1_size = vSize(p0p1);
|
||||
if (dist_left_over >= p0p1_size - corner_disregard_p1)
|
||||
{
|
||||
dist_left_over -= p0p1_size;
|
||||
return;
|
||||
}
|
||||
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
int64_t dist_last_point = -1; // p0p1_size * 2 - dist_left_over; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size - corner_disregard_p1; p0pa_dist += settings.point_distance)
|
||||
{
|
||||
assert(p0pa_dist >= 0);
|
||||
assert(p0pa_dist <= p0p1_size);
|
||||
MatCoord mat_coord_now = mat.start;
|
||||
mat_coord_now.coords = mat.start.coords + (mat.end.coords - mat.start.coords) * p0pa_dist / p0p1_size;
|
||||
float val = mat_coord_now.getColor(settings.color_usage);
|
||||
int offset = getOffset(val, slicer_segment.faceIndex);
|
||||
Point fuzz = normal(perp_to_p0p1, offset);
|
||||
Point pa = p0 + normal(p0p1, p0pa_dist) - fuzz;
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
assert(dist_last_point >= 0 && "above loop should have run at least once!");
|
||||
assert(p0p1_size > dist_last_point);
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
assert(dist_left_over <= settings.point_distance + corner_disregard_p1);
|
||||
}
|
||||
|
||||
|
||||
void TextureBumpMapProcessor::processBumpMap(Polygons& layer_polygons, unsigned int layer_nr)
|
||||
{
|
||||
if (layer_polygons.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygons preprocessed;
|
||||
for (PolygonRef poly : layer_polygons)
|
||||
{ // remove duplicate points
|
||||
PolygonRef preprocessed_poly = preprocessed.newPoly();
|
||||
Point p0 = poly.back();
|
||||
for (const Point p1 : poly)
|
||||
{
|
||||
if (p1 == p0)
|
||||
continue;
|
||||
preprocessed_poly.add(p1);
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
Polygons results;
|
||||
for (PolygonRef poly : preprocessed)
|
||||
{
|
||||
if (poly.size() < 3)
|
||||
{
|
||||
results.add(poly);
|
||||
continue;
|
||||
}
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
std::vector<std::optional<TexturedFaceSlice>> texture_poly;
|
||||
{
|
||||
Point p0 = poly.back();
|
||||
for (Point& p1 : poly)
|
||||
{
|
||||
texture_poly.emplace_back(getTexturedFaceSlice(p0, p1));
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
coord_t corner_disregard_p0 = getCornerDisregard(poly[poly.size() - 2], poly.back(), poly[0], texture_poly.back(), texture_poly[0]);; // TODO
|
||||
coord_t dist_left_over = (settings.point_distance / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{ // 'a' is the (next) new point between p0 and p1
|
||||
Point& p1 = poly[point_idx];
|
||||
unsigned int next_point_idx = (point_idx + 1 == poly.size())? 0 : point_idx + 1;
|
||||
Point& p2 = poly[next_point_idx];
|
||||
if (*p0 == p1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::optional<TexturedFaceSlice>& textured_face_slice = texture_poly[point_idx];
|
||||
std::optional<TexturedFaceSlice>& next_textured_face_slice = texture_poly[next_point_idx];
|
||||
|
||||
coord_t corner_disregard_p1 = getCornerDisregard(*p0, p1, p2, textured_face_slice, next_textured_face_slice); // TODO
|
||||
if (dist_left_over < corner_disregard_p0)
|
||||
{
|
||||
dist_left_over = corner_disregard_p0;
|
||||
}
|
||||
|
||||
if (textured_face_slice)
|
||||
{
|
||||
processSegmentBumpMap(layer_nr, textured_face_slice->face_segment, textured_face_slice->mat_segment, *p0, p1, dist_left_over, corner_disregard_p1, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
coord_t p0p1_size2 = vSize2(p1 - *p0);
|
||||
if (p0p1_size2 < dist_left_over * dist_left_over)
|
||||
{
|
||||
dist_left_over -= sqrt(p0p1_size2);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(*p0);
|
||||
result.emplace_back(p1);
|
||||
dist_left_over = settings.point_distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (corner_disregard_p1 == 0
|
||||
&& (textured_face_slice || next_textured_face_slice)
|
||||
&& (textured_face_slice || !shorterThen(p1 - *p0, SLICE_SEGMENT_SNAP_GAP)) // don't introduce corner points for gap closer poly segments
|
||||
&& (next_textured_face_slice || !shorterThen(p2 - p1, SLICE_SEGMENT_SNAP_GAP)) // don't introduce corner points for gap closer poly segments
|
||||
)
|
||||
{ // add point for outward corner
|
||||
// TODO: remove code duplication with getCornerDisregard
|
||||
coord_t offset = getCornerOffset(textured_face_slice, next_textured_face_slice);
|
||||
Point v01 = p1 - *p0;
|
||||
Point v12 = p2 - p1;
|
||||
Point n01 = normal(turn90CCW(v01), -1000);
|
||||
Point n12 = normal(turn90CCW(v12), -1000);
|
||||
Point corner_normal = normal(n01 + n12, offset);
|
||||
result.add(p1 + corner_normal);
|
||||
}
|
||||
p0 = &p1;
|
||||
corner_disregard_p0 = corner_disregard_p1;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
if (point_idx == 0) { break; }
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
for (Point& p : poly)
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
// a negative offset on two sides of a corner, may introduce complexities in the model which should be removed:
|
||||
// ^↘
|
||||
// ^ ↘
|
||||
// <<<<<<<<^<<<< should become <<<<<<<<
|
||||
// ^ ^
|
||||
// ^ ^
|
||||
// ^ ^
|
||||
layer_polygons = results.removeComplexParts();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,150 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_TEXTURE_BUMP_MAP_PROCESSOR_H
|
||||
#define TEXTURE_PROCESSING_TEXTURE_BUMP_MAP_PROCESSOR_H
|
||||
|
||||
#include <vector>
|
||||
#include <math.h> // tan
|
||||
|
||||
#include "../utils/polygon.h"
|
||||
#include "../utils/optional.h"
|
||||
#include "../utils/SparsePointGrid.h"
|
||||
|
||||
#include "../settings/settings.h"
|
||||
|
||||
#include "../slicer/SlicerSegment.h"
|
||||
#include "TexturedMesh.h"
|
||||
#include "FaceNormalStorage.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class TextureBumpMapProcessor
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Helper class to retrieve and store texture to bump map settings
|
||||
*/
|
||||
struct Settings
|
||||
{
|
||||
coord_t layer_height;
|
||||
coord_t point_distance;
|
||||
coord_t amplitude;
|
||||
coord_t offset;
|
||||
bool alternate;
|
||||
float face_angle_correction;
|
||||
float max_tan_correction_angle;
|
||||
ColourUsage color_usage;
|
||||
Settings(SettingsBaseVirtual* settings_base)
|
||||
: layer_height(settings_base->getSettingInMicrons("layer_height"))
|
||||
, point_distance(settings_base->getSettingInMicrons("bump_map_point_dist"))
|
||||
, amplitude(settings_base->getSettingInMicrons("bump_map_amplitude"))
|
||||
, offset(settings_base->getSettingInMicrons("bump_map_offset"))
|
||||
, alternate(settings_base->getSettingBoolean("bump_map_alternate"))
|
||||
, face_angle_correction(settings_base->getSettingAsRatio("bump_map_face_angle_correction"))
|
||||
, max_tan_correction_angle(std::tan(0.5 * M_PI - settings_base->getSettingInAngleRadians("bump_map_angle_correction_min")))
|
||||
, color_usage(settings_base->getSettingAsColourUsage("bump_map_texture_color"))
|
||||
{
|
||||
}
|
||||
};
|
||||
/*!
|
||||
* default constructor
|
||||
*
|
||||
* initializes the \ref SparseGrid::cell_size of \ref TextureBumpMapProcessor::loc_to_slice
|
||||
*
|
||||
* \param settings The settings with which to \ref TextureBumpMapProcessor::processBumpMap
|
||||
*/
|
||||
TextureBumpMapProcessor(TexturedMesh* mesh, const Settings settings, FaceNormalStorage* face_normal_storage);
|
||||
|
||||
/*!
|
||||
* Process the texture bump map.
|
||||
* Change the polygons in a layer
|
||||
*
|
||||
* \warning Where no texture is present, no offset is applied to the outer boundary!
|
||||
* Such segments are copied to the result as is
|
||||
*
|
||||
* \param[in,out] layer_polygons The polygons to be offsetted by texture color values
|
||||
* \param layer_nr The layer nr for which we are processing the bump map
|
||||
*/
|
||||
void processBumpMap(Polygons& layer_polygons, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Register that a particular face was sliced to a particular texture segment.
|
||||
* \param face_segment The geometrical segment of the face
|
||||
* \param texture_segment The corresponding texture coordinates
|
||||
*/
|
||||
void registerTexturedFaceSlice(SlicerSegment face_segment, MatSegment texture_segment);
|
||||
protected:
|
||||
/*!
|
||||
* A sliced segment in combination with the corresponding texture slice.
|
||||
*/
|
||||
struct TexturedFaceSlice
|
||||
{
|
||||
SlicerSegment face_segment;
|
||||
MatSegment mat_segment;
|
||||
};
|
||||
|
||||
TexturedMesh* mesh;
|
||||
|
||||
/*!
|
||||
* The settings with which to \ref TextureBumpMapProcessor::processBumpMap
|
||||
*/
|
||||
Settings settings;
|
||||
|
||||
/*!
|
||||
* The face normal statistics to correct offsets for slanted faces - if provided
|
||||
*
|
||||
* This is stored as a pointer so that the default assignment operator = can be defined automatically.
|
||||
*/
|
||||
FaceNormalStorage* face_normal_storage;
|
||||
|
||||
/*!
|
||||
* A grid to efficiently look op which texture segment best fits the slicer segment.
|
||||
*/
|
||||
SparseGrid<TexturedFaceSlice> loc_to_slice;
|
||||
|
||||
/*!
|
||||
* Get the offset to be applied at a given location
|
||||
*/
|
||||
coord_t getOffset(const float color, const int face_idx);
|
||||
|
||||
/*!
|
||||
* Get the offset to be applied at a given corner
|
||||
*
|
||||
* Computes the average offset from the end of \p textured_face_slice and start of \p next_textured_face_slice
|
||||
* If either of those is not present, the \ref TextureBumpMapProcessor::Settings::default_color is used for that segment
|
||||
*
|
||||
* \warning Where no texture is present, no offset is applied to the outer boundary!
|
||||
*
|
||||
* \param textured_face_slice From which to determine the offset at the end of the line segment - or default to zero
|
||||
* \param next_textured_face_slice From which to determine the offset at the start of the line segment - or default to zero
|
||||
*/
|
||||
coord_t getCornerOffset(std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& textured_face_slice, std::optional<TextureBumpMapProcessor::TexturedFaceSlice>& next_textured_face_slice);
|
||||
|
||||
/*!
|
||||
* Get how much of a corner to skip generating offsetted indices for inner corners,
|
||||
* because those points would be removed by the offset itseld
|
||||
*/
|
||||
coord_t getCornerDisregard(Point p0, Point p1, Point p2, std::optional<TexturedFaceSlice>& textured_face_slice, std::optional<TexturedFaceSlice>& next_textured_face_slice);
|
||||
|
||||
/*!
|
||||
* Get the TexturedFaceSlice corresponding to an outline segment
|
||||
*
|
||||
* Note that due to snapping in the \ref Slicer::makePolygons function, an outline segment may be a bit different from the originally sliced SlicerSegment
|
||||
*
|
||||
* \param p0 The start of the segment
|
||||
* \param p1 The end of the segment
|
||||
*/
|
||||
std::optional<TexturedFaceSlice> getTexturedFaceSlice(Point p0, Point p1);
|
||||
|
||||
/*!
|
||||
*
|
||||
* \param layer_nr The layer number for which we process the bump map
|
||||
* \param slicer_segment The segment closest matching \p p0 - \p p1
|
||||
* \param corner_disregard_p1 The distance at the end of p0p1 in which not to place offsetted points
|
||||
*/
|
||||
void processSegmentBumpMap(unsigned int layer_nr, const SlicerSegment& slicer_segment, const MatSegment& mat, const Point p0, const Point p1, coord_t& dist_left_over, coord_t corner_disregard_p1, PolygonRef result);
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_TEXTURE_BUMP_MAP_PROCESSOR_H
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "TextureProximityProcessor.h"
|
||||
|
||||
#include <algorithm> // swap
|
||||
#include <functional> // function
|
||||
|
||||
#include "../utils/optional.h"
|
||||
#include "../utils/linearAlg2D.h"
|
||||
#include "../slicer/SlicerSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
TextureProximityProcessor::TextureProximityProcessor(const TextureProximityProcessor::Settings settings, unsigned int slice_layer_count)
|
||||
: settings(settings)
|
||||
{
|
||||
loc_to_slice.resize(slice_layer_count, SparseLineGrid<TexturedFaceSlice, TexturedFaceSliceLocator>(settings.proximity));
|
||||
}
|
||||
|
||||
|
||||
void TextureProximityProcessor::registerTexturedFaceSlice(SlicerSegment face_segment, MatSegment texture_segment, unsigned int layer_nr)
|
||||
{
|
||||
TexturedFaceSlice slice{face_segment, texture_segment};
|
||||
assert((int)layer_nr >= 0 && layer_nr < loc_to_slice.size());
|
||||
loc_to_slice[layer_nr].insert(slice);
|
||||
}
|
||||
|
||||
float TextureProximityProcessor::getColor(const Point location, const unsigned int layer_nr, ColourUsage color, float default_color)
|
||||
{
|
||||
assert((int)layer_nr >= 0 && layer_nr < loc_to_slice.size());
|
||||
SparseLineGrid<TexturedFaceSlice, TexturedFaceSliceLocator> grid = loc_to_slice[layer_nr];
|
||||
|
||||
coord_t best_dist2 = std::numeric_limits<coord_t>::max();
|
||||
std::optional<TexturedFaceSlice> best;
|
||||
std::function<bool (const TexturedFaceSlice& in)> process_func = [location, &best_dist2, &best](const TexturedFaceSlice& in)
|
||||
{
|
||||
coord_t dist2 = LinearAlg2D::getDist2FromLineSegment(in.face_segment.start, location, in.face_segment.end);
|
||||
if (dist2 < best_dist2)
|
||||
{
|
||||
best_dist2 = dist2;
|
||||
best = in;
|
||||
}
|
||||
return true; // keep going, we're not sure whether we have found the best yet
|
||||
};
|
||||
|
||||
grid.processNearby(location, settings.proximity, process_func);
|
||||
|
||||
if (best_dist2 > settings.proximity * settings.proximity * 4)
|
||||
{
|
||||
return default_color;
|
||||
}
|
||||
assert(best && "given that dist2 != max int this variable should have been innitialized");
|
||||
const Point p0 = best->face_segment.start;
|
||||
const Point p1 = best->face_segment.end;
|
||||
const Point x = location;
|
||||
// Point r = resulting point on the nearest segment, nearest to [location]
|
||||
const MatSegment mat_segment = best->mat_segment;
|
||||
|
||||
const Point v01 = p1 - p0;
|
||||
const Point v0x = x - p0;
|
||||
const coord_t v01_length2 = vSize2(v01);
|
||||
if (v01_length2 <= 4)
|
||||
{
|
||||
return mat_segment.start.getColor(color);
|
||||
}
|
||||
|
||||
const coord_t dot_prod = dot(v0x, v01);
|
||||
const int64_t v0r_length2 = dot_prod * dot_prod / v01_length2;
|
||||
if (v0r_length2 <= 0)
|
||||
{
|
||||
return mat_segment.start.getColor(color);
|
||||
}
|
||||
if (v0r_length2 >= v01_length2)
|
||||
{
|
||||
return mat_segment.end.getColor(color);
|
||||
}
|
||||
const coord_t v0r_length = sqrt(v0r_length2);
|
||||
const coord_t v01_length = sqrt(v01_length2);
|
||||
MatCoord mat_in_between = mat_segment.start;
|
||||
mat_in_between.coords = mat_segment.start.coords + (mat_segment.end.coords - mat_segment.start.coords) * v0r_length / v01_length;
|
||||
return mat_in_between.getColor(color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,93 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_TEXTURE_PROXIMITY_PROCESSOR_H
|
||||
#define TEXTURE_PROCESSING_TEXTURE_PROXIMITY_PROCESSOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../utils/intpoint.h"
|
||||
#include "../utils/SparseLineGrid.h"
|
||||
|
||||
#include "../settings/settings.h"
|
||||
|
||||
#include "../slicer/SlicerSegment.h"
|
||||
#include "TexturedMesh.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Class for recording texture coordinates at places where textures are defined, for later looking in the proximity of a texture.
|
||||
*/
|
||||
class TextureProximityProcessor
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Helper class to retrieve and store texture to bump map settings
|
||||
*/
|
||||
struct Settings
|
||||
{
|
||||
coord_t proximity; //!< The distance within which to search for nearby texture
|
||||
Settings(coord_t proximity)
|
||||
: proximity(proximity)
|
||||
{
|
||||
}
|
||||
};
|
||||
/*!
|
||||
* default constructor
|
||||
*
|
||||
* initializes the \ref SparseGrid::cell_size of \ref TextureProximityProcessor::loc_to_slice
|
||||
*
|
||||
* \param settings The settings with which to \ref TextureProximityProcessor::processBumpMap
|
||||
*/
|
||||
TextureProximityProcessor(const Settings settings, unsigned int slice_layer_count);
|
||||
|
||||
/*!
|
||||
* Register that a particular face was sliced to a particular texture segment.
|
||||
* \param face_segment The geometrical segment of the face
|
||||
* \param texture_segment The corresponding texture coordinates
|
||||
* \param layer_nr The layer for which to register a face being sliced
|
||||
*/
|
||||
void registerTexturedFaceSlice(SlicerSegment face_segment, MatSegment texture_segment, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
*
|
||||
* \param default_color Default color where no texture is present
|
||||
*/
|
||||
float getColor(const Point location, const unsigned int layer_nr, ColourUsage color, float default_color);
|
||||
protected:
|
||||
/*!
|
||||
* A sliced segment in combination with the corresponding texture slice.
|
||||
*/
|
||||
struct TexturedFaceSlice
|
||||
{
|
||||
SlicerSegment face_segment;
|
||||
MatSegment mat_segment;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Locator to find the line segment of a \ref TexturedFaceSlice
|
||||
*/
|
||||
struct TexturedFaceSliceLocator
|
||||
{
|
||||
std::pair<Point, Point> operator()(const TexturedFaceSlice& elem) const
|
||||
{
|
||||
return std::make_pair(elem.face_segment.start, elem.face_segment.end);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* The settings with which to \ref TextureBumpMapProcessor::processBumpMap
|
||||
*/
|
||||
Settings settings;
|
||||
|
||||
/*!
|
||||
* A grid to efficiently look op which texture segment best fits the slicer segment.
|
||||
*
|
||||
* A vector of elements for each layer
|
||||
*/
|
||||
std::vector<SparseLineGrid<TexturedFaceSlice, TexturedFaceSliceLocator>> loc_to_slice;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_TEXTURE_PROXIMITY_PROCESSOR_H
|
||||
@@ -0,0 +1,144 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "TexturedMesh.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <math.h>
|
||||
|
||||
#include "../utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
TexturedMesh::TexturedMesh(SettingsBaseVirtual* sb)
|
||||
: Mesh(sb)
|
||||
, current_mat(-1) // not set yet
|
||||
{
|
||||
}
|
||||
|
||||
void TexturedMesh::addTextureCoord(float x, float y)
|
||||
{
|
||||
// some textures use wrapping for some unholy reason
|
||||
// unwrap for texture coordinates to fall within [0,1]
|
||||
if (x > 1.0f || x < 0.0f)
|
||||
{ // only apply fmod when more than 1.0
|
||||
x = fmod(x, 1.0f);
|
||||
if (x < 0.0)
|
||||
{
|
||||
x += 1.0f;
|
||||
}
|
||||
}
|
||||
if (y > 1.0f || y < 0.0f)
|
||||
{ // only apply fmod when more than 1.0
|
||||
y = fmod(y, 1.0f);
|
||||
if (y < 0.0)
|
||||
{
|
||||
y += 1.0f;
|
||||
}
|
||||
}
|
||||
texture_coords.emplace_back(x, y);
|
||||
}
|
||||
|
||||
void TexturedMesh::addFace(int vi0, int vi1, int vi2, int ti0, int ti1, int ti2)
|
||||
{
|
||||
if (vi0 < -1)
|
||||
{
|
||||
vi0 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (vi1 < -1)
|
||||
{
|
||||
vi1 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (vi2 < -1)
|
||||
{
|
||||
vi2 = Mesh::faces.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti0 < -1)
|
||||
{
|
||||
ti0 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti1 < -1)
|
||||
{
|
||||
ti1 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
if (ti2 < -1)
|
||||
{
|
||||
ti2 = texture_coords.size() + vi0 + 1; // + 1 because of relative indexing doesn't start counting from 1
|
||||
}
|
||||
bool made_new_face = Mesh::addFace(vi0, vi1, vi2);
|
||||
if (made_new_face)
|
||||
{
|
||||
face_texture_indices.emplace_back(ti0, ti1, ti2, current_mat);
|
||||
assert(Mesh::faces.size() == face_texture_indices.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool TexturedMesh::setMaterial(std::string name)
|
||||
{
|
||||
current_mat = material_base.getMatId(name);
|
||||
return current_mat >= 0;
|
||||
}
|
||||
|
||||
Material* TexturedMesh::addMaterial(std::string name)
|
||||
{
|
||||
return material_base.add(name);
|
||||
}
|
||||
|
||||
|
||||
bool TexturedMesh::getFaceEdgeMatCoord(unsigned int face_idx, int64_t z, unsigned int p0_idx, unsigned int p1_idx, MatCoord& result) const
|
||||
{
|
||||
if (face_idx >= face_texture_indices.size() || face_idx >= faces.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FaceTextureCoordIndices texture_idxs = face_texture_indices[face_idx];
|
||||
if (texture_idxs.index[0] < 0 || texture_idxs.index[1] < 0 || texture_idxs.index[2] < 0 || texture_idxs.mat_id < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const MeshFace& face = faces[face_idx];
|
||||
Point3 p0(vertices[face.vertex_index[p0_idx]].p);
|
||||
Point3 p1(vertices[face.vertex_index[p1_idx]].p);
|
||||
|
||||
float dzp0 = z - p0.z;
|
||||
float dp0p1 = p1.z - p0.z;
|
||||
|
||||
if (dzp0 * dp0p1 < 0)
|
||||
{ // z doesn't lie between p0 and p1
|
||||
return false;
|
||||
}
|
||||
if (dzp0 == 0)
|
||||
{ // edge is not cut by horizontal plane!
|
||||
return false;
|
||||
}
|
||||
float ratio = INT2MM(dzp0) / INT2MM(dp0p1);
|
||||
|
||||
FPoint t0 = texture_coords[texture_idxs.index[p0_idx]];
|
||||
FPoint t1 = texture_coords[texture_idxs.index[p1_idx]];
|
||||
|
||||
result.mat = material_base.getMat(texture_idxs.mat_id);
|
||||
result.coords.x = t0.x + (t1.x - t0.x) * ratio;
|
||||
result.coords.y = t0.y + (t1.y - t0.y) * ratio;
|
||||
|
||||
if (result.coords.x > 1.001 || result.coords.x < -0.001 || result.coords.y > 1.001 || result.coords.y < -0.001)
|
||||
{
|
||||
logError("WARNING: wrapping material to outside image!\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TexturedMesh::sliceFaceTexture(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result) const
|
||||
{
|
||||
if (!getFaceEdgeMatCoord(face_idx, z, idx_shared, idx_first, result.start))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!getFaceEdgeMatCoord(face_idx, z, idx_shared, idx_second, result.end))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,77 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef TEXTURE_PROCESSING_TEXTURED_MESH_H
|
||||
#define TEXTURE_PROCESSING_TEXTURED_MESH_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "MaterialBase.h"
|
||||
|
||||
#include "../mesh.h"
|
||||
#include "../utils/intpoint.h"
|
||||
#include "MatSegment.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* A mesh with bitmap textures to it.
|
||||
*
|
||||
* material coordinates are defined separately, and can be reused for different bitmap textures
|
||||
*/
|
||||
class TexturedMesh : public Mesh
|
||||
{
|
||||
public:
|
||||
TexturedMesh(SettingsBaseVirtual* sb);
|
||||
|
||||
|
||||
/*!
|
||||
*
|
||||
*/
|
||||
struct FaceTextureCoordIndices
|
||||
{
|
||||
int index[3]; //!< indices into texture_coords or -1 if no texture data available
|
||||
int mat_id; //!< Material id
|
||||
FaceTextureCoordIndices(int i1, int i2, int i3, int mat_id)
|
||||
: mat_id(mat_id)
|
||||
{
|
||||
index[0] = i1;
|
||||
index[1] = i2;
|
||||
index[2] = i3;
|
||||
}
|
||||
};
|
||||
void addTextureCoord(float x, float y);
|
||||
void addFace(int vi0, int vi1, int vi2, int ti0, int ti1, int ti2);
|
||||
using Mesh::addFace; // otherwise above addFace would shadow the parent addFace
|
||||
|
||||
bool setMaterial(std::string name); //!< set the material to be used in the comming data to be loaded
|
||||
Material* addMaterial(std::string name);
|
||||
|
||||
/*!
|
||||
* \return Whether a texture line segment has been created
|
||||
*/
|
||||
bool sliceFaceTexture(unsigned int face_idx, unsigned int idx_shared, unsigned int idx_first, unsigned int idx_second, int32_t z, Point segment_start, Point segment_end, MatSegment& result) const;
|
||||
|
||||
protected:
|
||||
std::vector<FPoint> texture_coords; //!< all texture coordinates by all faces
|
||||
std::vector<FaceTextureCoordIndices> face_texture_indices; //!< for each face the corresponding texture coordinates in TexturedMesh::texture_coords
|
||||
// TODO clean up above lists when super class clear() is called
|
||||
// TODO when to clean up below material base?
|
||||
MaterialBase material_base;
|
||||
/*!
|
||||
* Get the material coordinate corresponding to the point on a plane cutting a given edge of the face.
|
||||
* \param face_idx The face for which to get the material coord
|
||||
* \param z The z of the horizontal plane cutting the face
|
||||
* \param p0_idx The index into the first vert of the edge
|
||||
* \param p1_idx The index into the second vert of the edge
|
||||
* \param result The resulting material Coordinates
|
||||
* \return Whether a Material coordinate is defined at the given location
|
||||
*/
|
||||
bool getFaceEdgeMatCoord(unsigned int face_idx, int64_t z, unsigned int p0_idx, unsigned int p1_idx, MatCoord& result) const;
|
||||
private:
|
||||
int current_mat; //!< material currently used in loading the face material info
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // TEXTURE_PROCESSING_TEXTURED_MESH_H
|
||||
@@ -24,7 +24,7 @@ AABB::AABB(const Polygons& polys)
|
||||
calculate(polys);
|
||||
}
|
||||
|
||||
AABB::AABB(const PolygonRef& poly)
|
||||
AABB::AABB(const PolygonRef poly)
|
||||
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
|
||||
{
|
||||
calculate(poly);
|
||||
@@ -43,7 +43,7 @@ void AABB::calculate(const Polygons& polys)
|
||||
}
|
||||
}
|
||||
|
||||
void AABB::calculate(const PolygonRef& poly)
|
||||
void AABB::calculate(const PolygonRef poly)
|
||||
{
|
||||
min = Point(POINT_MAX, POINT_MAX);
|
||||
max = Point(POINT_MIN, POINT_MIN);
|
||||
|
||||
+2
-2
@@ -20,10 +20,10 @@ public:
|
||||
AABB(); //!< initializes with invalid min and max
|
||||
AABB(Point& min, Point& max); //!< initializes with given min and max
|
||||
AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons
|
||||
AABB(const PolygonRef& poly); //!< Computes the boundary box for the given polygons
|
||||
AABB(const PolygonRef poly); //!< Computes the boundary box for the given polygons
|
||||
|
||||
void calculate(const Polygons& polys); //!< Calculates the aabb for the given polygons (throws away old min and max data of this aabb)
|
||||
void calculate(const PolygonRef& poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
|
||||
void calculate(const PolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
|
||||
|
||||
/*!
|
||||
* Check whether this aabb overlaps with another.
|
||||
|
||||
@@ -37,12 +37,6 @@ void AABB3D::include(Point3 p)
|
||||
max.z = std::max(max.z, p.z);
|
||||
}
|
||||
|
||||
void AABB3D::includeZ(int32_t z)
|
||||
{
|
||||
min.z = std::min(min.z, z);
|
||||
max.z = std::max(max.z, z);
|
||||
}
|
||||
|
||||
void AABB3D::offset(Point3 offset)
|
||||
{
|
||||
min += offset;
|
||||
|
||||
@@ -38,14 +38,6 @@ struct AABB3D
|
||||
*/
|
||||
void include(Point3 p);
|
||||
|
||||
/*!
|
||||
* Expand the AABB3D to include a z-coordinate.
|
||||
*
|
||||
* This is for including a point of which the X and Y coordinates are
|
||||
* unknown but known to already be included in the bounding box.
|
||||
*/
|
||||
void includeZ(int32_t z);
|
||||
|
||||
/*!
|
||||
* Offset the coordinates of the bounding box.
|
||||
* \param offset The offset with which to offset the AABB3D.
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
|
||||
#ifndef UTILS_F_POINT_H
|
||||
#define UTILS_F_POINT_H
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <iostream> // auto-serialization / auto-toString() '<<'
|
||||
|
||||
namespace cura
|
||||
{
|
||||
/*!
|
||||
* 2D coordinates represented by floats
|
||||
*/
|
||||
class FPoint
|
||||
{
|
||||
public:
|
||||
float x, y; //!< Coordinates
|
||||
FPoint() //!< non-initializing constructor
|
||||
{}
|
||||
FPoint(float x, float y) //!< constructor
|
||||
: x(x)
|
||||
, y(y)
|
||||
{}
|
||||
|
||||
FPoint operator+(const FPoint& p) const { return FPoint(x+p.x, y+p.y); }
|
||||
FPoint operator-(const FPoint& p) const { return FPoint(x-p.x, y-p.y); }
|
||||
FPoint operator/(const float i) const { return FPoint(x/i, y/i); }
|
||||
FPoint operator*(const float i) const { return FPoint(x*i, y*i); }
|
||||
|
||||
FPoint& operator += (const FPoint& p) { x += p.x; y += p.y; return *this; }
|
||||
FPoint& operator -= (const FPoint& p) { x -= p.x; y -= p.y; return *this; }
|
||||
|
||||
bool operator==(const FPoint& p) const { return x == p.x && y == p.y; }
|
||||
bool operator!=(const FPoint& p) const { return x != p.x || y != p.y; }
|
||||
|
||||
/*!
|
||||
* output to string stream in standard format
|
||||
*/
|
||||
template<class CharT, class TraitsT>
|
||||
friend
|
||||
std::basic_ostream<CharT, TraitsT>&
|
||||
operator <<(std::basic_ostream<CharT, TraitsT>& os, const FPoint& p)
|
||||
{
|
||||
return os << "(" << p.x << ", " << p.y << ")";
|
||||
}
|
||||
|
||||
/*!
|
||||
* squared vector size
|
||||
*/
|
||||
float vSize2() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
/*!
|
||||
* vector size
|
||||
*/
|
||||
float vSize() const
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
|
||||
/*!
|
||||
* dot product
|
||||
*/
|
||||
float dot(const FPoint& p) const
|
||||
{
|
||||
return x * p.x + y * p.y;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // UTILS_F_POINT_H
|
||||
+40
-21
@@ -10,49 +10,68 @@ namespace cura
|
||||
{
|
||||
|
||||
|
||||
void ListPolyIt::convertPolygonsToLists(Polygons& polys, ListPolygons& result)
|
||||
void ListPolyIt::convertPolygonsToLists(Polygons& polys, ListPolygons& result, bool remove_duplicates)
|
||||
{
|
||||
for (PolygonRef poly : polys)
|
||||
{
|
||||
result.emplace_back();
|
||||
convertPolygonToList(poly, result.back());
|
||||
convertPolygonToList(poly, result.back(), remove_duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
void ListPolyIt::convertPolygonToList(const PolygonRef& poly, ListPolygon& result)
|
||||
void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result, bool remove_duplicates)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Point last = poly.back();
|
||||
#endif // DEBUG
|
||||
for (const Point& p : poly)
|
||||
if (remove_duplicates)
|
||||
{
|
||||
result.push_back(p);
|
||||
#ifdef DEBUG
|
||||
// usually polygons shouldn't have such degenerate verts
|
||||
// in PolygonProximityLinker (where this function is (also) used) it is
|
||||
// required to not have degenerate verts, because verts are mapped
|
||||
// to links, but if two different verts are at the same place the mapping fails.
|
||||
assert(p != last);
|
||||
last = p;
|
||||
#endif // DEBUG
|
||||
Point last = poly.back();
|
||||
for (Point& p : poly)
|
||||
{
|
||||
if (p != last)
|
||||
{
|
||||
result.push_back(p);
|
||||
last = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Point& p : poly)
|
||||
{
|
||||
result.push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons)
|
||||
void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons, bool remove_duplicates)
|
||||
{
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
polygons[poly_idx].clear();
|
||||
convertListPolygonToPolygon(list_polygons[poly_idx], polygons[poly_idx]);
|
||||
convertListPolygonToPolygon(list_polygons[poly_idx], polygons[poly_idx], remove_duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon)
|
||||
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon, bool remove_duplicates)
|
||||
{
|
||||
for (Point& p : list_polygon)
|
||||
if (remove_duplicates)
|
||||
{
|
||||
polygon.add(p);
|
||||
Point last = list_polygon.back();
|
||||
for (Point& p : list_polygon)
|
||||
{
|
||||
if (p != last)
|
||||
{
|
||||
polygon.add(p);
|
||||
last = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Point& p : list_polygon)
|
||||
{
|
||||
polygon.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,29 +93,33 @@ public:
|
||||
*
|
||||
* \param polys The polygons to convert
|
||||
* \param result The converted polygons
|
||||
* \param remove_duplicates Whether to skip the conversion of duplicate consecutive points in the input
|
||||
*/
|
||||
static void convertPolygonsToLists(Polygons& polys, ListPolygons& result);
|
||||
static void convertPolygonsToLists(Polygons& polys, ListPolygons& result, bool remove_duplicates = false);
|
||||
/*!
|
||||
* Convert Polygons to ListPolygons
|
||||
*
|
||||
* \param polys The polygons to convert
|
||||
* \param result The converted polygons
|
||||
* \param remove_duplicates Whether to skip the conversion of duplicate consecutive points in the input
|
||||
*/
|
||||
static void convertPolygonToList(const PolygonRef& poly, ListPolygon& result);
|
||||
static void convertPolygonToList(PolygonRef poly, ListPolygon& result, bool remove_duplicates = false);
|
||||
/*!
|
||||
* Convert ListPolygons to Polygons
|
||||
*
|
||||
* \param list_polygons The polygons to convert
|
||||
* \param polygons The converted polygons
|
||||
* \param remove_duplicates Whether to skip the conversion of duplicate consecutive points in the input
|
||||
*/
|
||||
static void convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons);
|
||||
static void convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons, bool remove_duplicates = false);
|
||||
/*!
|
||||
* Convert ListPolygons to Polygons
|
||||
*
|
||||
* \param list_polygons The polygons to convert
|
||||
* \param polygons The converted polygons
|
||||
* \param remove_duplicates Whether to skip the conversion of duplicate consecutive points in the input
|
||||
*/
|
||||
static void convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon);
|
||||
static void convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon, bool remove_duplicates = false);
|
||||
|
||||
/*!
|
||||
* Insert a point into a ListPolygon if it's not a duplicate of the point before or the point after.
|
||||
|
||||
@@ -21,7 +21,8 @@ PolygonProximityLinker::PolygonProximityLinker(Polygons& polygons, int proximity
|
||||
proximity_point_links.reserve(polygons.pointCount()); // When the whole model consists of thin walls, there will generally be a link for every point, plus some endings minus some points which map to eachother
|
||||
|
||||
// convert to list polygons for insertion of points
|
||||
ListPolyIt::convertPolygonsToLists(polygons, list_polygons);
|
||||
constexpr bool remove_duplicates = true;
|
||||
ListPolyIt::convertPolygonsToLists(polygons, list_polygons, remove_duplicates);
|
||||
|
||||
// link each corner to itself
|
||||
addSharpCorners();
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
/*!
|
||||
* Get the polygon to which this PolygonsPointIndex refers
|
||||
*/
|
||||
const ConstPolygonRef getPolygon() const
|
||||
const PolygonRef getPolygon() const
|
||||
{
|
||||
return (*polygons)[poly_idx];
|
||||
}
|
||||
|
||||
+5
-5
@@ -93,7 +93,7 @@ public:
|
||||
{
|
||||
for(unsigned int j=0;j<parts.size();j++)
|
||||
{
|
||||
Polygon poly = PolygonRef{parts[j]};
|
||||
Polygon poly = parts[j];
|
||||
fprintf(out, "<polygon points=\"");
|
||||
for(Point& p : poly)
|
||||
{
|
||||
@@ -130,9 +130,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void writePoints(ConstPolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
|
||||
{
|
||||
for (const Point& p : poly)
|
||||
for (Point& p : poly)
|
||||
{
|
||||
writePoint(p, write_coords, size, color);
|
||||
}
|
||||
@@ -209,12 +209,12 @@ public:
|
||||
}
|
||||
void writePolygons(const Polygons& polys, Color color = Color::BLACK)
|
||||
{
|
||||
for (ConstPolygonRef poly : polys)
|
||||
for (const PolygonRef poly : const_cast<Polygons&>(polys))
|
||||
{
|
||||
writePolygon(poly, color);
|
||||
}
|
||||
}
|
||||
void writePolygon(ConstPolygonRef poly, Color color = Color::BLACK)
|
||||
void writePolygon(const PolygonRef poly, Color color = Color::BLACK)
|
||||
{
|
||||
Point p0 = poly.back();
|
||||
for (Point p1 : poly)
|
||||
|
||||
@@ -14,9 +14,6 @@ namespace cura {
|
||||
|
||||
/*! \brief Sparse grid which can locate spatially nearby elements efficiently.
|
||||
*
|
||||
* \note This is an abstract template class which doesn't have any functions to insert elements.
|
||||
* \see SparsePointGrid
|
||||
*
|
||||
* \tparam ElemT The element type to store.
|
||||
*/
|
||||
template<class ElemT>
|
||||
@@ -24,6 +21,11 @@ class SparseGrid
|
||||
{
|
||||
public:
|
||||
using Elem = ElemT;
|
||||
protected:
|
||||
using GridPoint = Point;
|
||||
using grid_coord_t = coord_t;
|
||||
using GridMap = std::unordered_multimap<GridPoint, Elem>;
|
||||
public:
|
||||
|
||||
/*! \brief Constructs a sparse grid with the specified cell size.
|
||||
*
|
||||
@@ -95,10 +97,50 @@ public:
|
||||
|
||||
coord_t getCellSize() const;
|
||||
|
||||
/*! \brief Inserts elem into the sparse grid.
|
||||
*
|
||||
* \param[in] location The location where to insert the element
|
||||
* \param[in] elem The element to be inserted.
|
||||
*/
|
||||
void insert(Point location, const Elem &elem);
|
||||
|
||||
class iterator
|
||||
{
|
||||
friend class SparseGrid<ElemT>;
|
||||
typename GridMap::iterator it;
|
||||
iterator(typename GridMap::iterator it)
|
||||
:it(it)
|
||||
{}
|
||||
public:
|
||||
iterator operator++() // pre-increment
|
||||
{
|
||||
++it;
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int) // post increment
|
||||
{
|
||||
iterator ret(it);
|
||||
++it;
|
||||
return ret;
|
||||
}
|
||||
Elem operator*()
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
bool operator==(iterator other)
|
||||
{
|
||||
return it == other.it;
|
||||
}
|
||||
bool operator!=(iterator other)
|
||||
{
|
||||
return it != other.it;
|
||||
}
|
||||
// TODO: fully implement iterator interface
|
||||
};
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
protected:
|
||||
using GridPoint = Point;
|
||||
using grid_coord_t = coord_t;
|
||||
using GridMap = std::unordered_multimap<GridPoint, Elem>;
|
||||
|
||||
/*! \brief Process elements from the cell indicated by \p grid_pt.
|
||||
*
|
||||
@@ -221,6 +263,26 @@ typename cura::coord_t SGI_THIS::toLowerCoord(const grid_coord_t& grid_coord) c
|
||||
return grid_coord * m_cell_size;
|
||||
}
|
||||
|
||||
SGI_TEMPLATE
|
||||
void SGI_THIS::insert(Point loc, const Elem &elem)
|
||||
{
|
||||
GridPoint grid_loc = toGridPoint(loc);
|
||||
|
||||
m_grid.emplace(grid_loc, elem);
|
||||
}
|
||||
|
||||
SGI_TEMPLATE
|
||||
typename SGI_THIS::iterator SGI_THIS::begin()
|
||||
{
|
||||
return iterator(m_grid.begin());
|
||||
}
|
||||
|
||||
SGI_TEMPLATE
|
||||
typename SGI_THIS::iterator SGI_THIS::end()
|
||||
{
|
||||
return iterator(m_grid.end());
|
||||
}
|
||||
|
||||
SGI_TEMPLATE
|
||||
bool SGI_THIS::processFromCell(
|
||||
const GridPoint &grid_pt,
|
||||
|
||||
@@ -62,9 +62,7 @@ SGI_TEMPLATE
|
||||
void SGI_THIS::insert(const Elem &elem)
|
||||
{
|
||||
Point loc = m_locator(elem);
|
||||
GridPoint grid_loc = SparseGrid<ElemT>::toGridPoint(loc);
|
||||
|
||||
SparseGrid<ElemT>::m_grid.emplace(grid_loc,elem);
|
||||
SparseGrid<ElemT>::insert(loc, elem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+29
-21
@@ -46,17 +46,19 @@ Integer points are used to avoid floating point rounding errors, and because Cli
|
||||
namespace cura
|
||||
{
|
||||
|
||||
using coord_t = ClipperLib::cInt;
|
||||
|
||||
class Point3
|
||||
{
|
||||
public:
|
||||
int32_t x,y,z;
|
||||
coord_t x,y,z;
|
||||
Point3() {}
|
||||
Point3(const int32_t _x, const int32_t _y, const int32_t _z): x(_x), y(_y), z(_z) {}
|
||||
Point3(const coord_t _x, const coord_t _y, const coord_t _z): x(_x), y(_y), z(_z) {}
|
||||
|
||||
Point3 operator+(const Point3& p) const { return Point3(x+p.x, y+p.y, z+p.z); }
|
||||
Point3 operator-(const Point3& p) const { return Point3(x-p.x, y-p.y, z-p.z); }
|
||||
Point3 operator/(const int32_t i) const { return Point3(x/i, y/i, z/i); }
|
||||
Point3 operator*(const int32_t i) const { return Point3(x*i, y*i, z*i); }
|
||||
Point3 operator/(const coord_t i) const { return Point3(x/i, y/i, z/i); }
|
||||
Point3 operator*(const coord_t i) const { return Point3(x*i, y*i, z*i); }
|
||||
Point3 operator*(const double d) const { return Point3(d*x, d*y, d*z); }
|
||||
|
||||
Point3& operator += (const Point3& p) { x += p.x; y += p.y; z += p.z; return *this; }
|
||||
@@ -75,14 +77,14 @@ public:
|
||||
}
|
||||
|
||||
|
||||
int32_t max() const
|
||||
coord_t max() const
|
||||
{
|
||||
if (x > y && x > z) return x;
|
||||
if (y > z) return y;
|
||||
return z;
|
||||
}
|
||||
|
||||
bool testLength(int32_t len) const
|
||||
bool testLength(coord_t len) const
|
||||
{
|
||||
if (x > len || x < -len)
|
||||
return false;
|
||||
@@ -93,12 +95,12 @@ public:
|
||||
return vSize2() <= len*len;
|
||||
}
|
||||
|
||||
int64_t vSize2() const
|
||||
coord_t vSize2() const
|
||||
{
|
||||
return int64_t(x)*int64_t(x)+int64_t(y)*int64_t(y)+int64_t(z)*int64_t(z);
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
int32_t vSize() const
|
||||
coord_t vSize() const
|
||||
{
|
||||
return sqrt(vSize2());
|
||||
}
|
||||
@@ -110,25 +112,33 @@ public:
|
||||
double fz = INT2MM(z);
|
||||
return sqrt(fx*fx+fy*fy+fz*fz);
|
||||
}
|
||||
/*! this function is deprecated because it can cause overflows for vectors which easily fit inside a printer. Use FPoint3.cross(a,b) instead. */
|
||||
DEPRECATED(Point3 cross(const Point3& p))
|
||||
Point3 cross(const Point3& p)
|
||||
{
|
||||
return Point3(
|
||||
y*p.z-z*p.y, /// dangerous for vectors longer than 4.6 cm !!!!!
|
||||
z*p.x-x*p.z, /// can cause overflows
|
||||
y*p.z-z*p.y,
|
||||
z*p.x-x*p.z,
|
||||
x*p.y-y*p.x);
|
||||
}
|
||||
|
||||
int64_t dot(const Point3& p) const
|
||||
coord_t dot(const Point3& p) const
|
||||
{
|
||||
return x*p.x + y*p.y + z*p.z;
|
||||
}
|
||||
|
||||
Point3 normal(coord_t desired_length) const
|
||||
{
|
||||
coord_t current_length = vSize();
|
||||
if (current_length < 1)
|
||||
{
|
||||
return Point3(0, 0, desired_length);
|
||||
}
|
||||
return *this * desired_length / current_length;
|
||||
}
|
||||
};
|
||||
|
||||
static Point3 no_point3(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
|
||||
static Point3 no_point3(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max());
|
||||
|
||||
inline Point3 operator*(const int32_t i, const Point3& rhs) {
|
||||
inline Point3 operator*(const coord_t i, const Point3& rhs) {
|
||||
return rhs * i;
|
||||
}
|
||||
|
||||
@@ -136,8 +146,6 @@ inline Point3 operator*(const double d, const Point3& rhs) {
|
||||
return rhs * d;
|
||||
}
|
||||
|
||||
using coord_t = ClipperLib::cInt;
|
||||
|
||||
/* 64bit Points are used mostly troughout the code, these are the 2D points from ClipperLib */
|
||||
typedef ClipperLib::IntPoint Point;
|
||||
|
||||
@@ -146,10 +154,10 @@ public:
|
||||
int X, Y;
|
||||
Point p() { return Point(X, Y); }
|
||||
};
|
||||
#define POINT_MIN std::numeric_limits<ClipperLib::cInt>::min()
|
||||
#define POINT_MAX std::numeric_limits<ClipperLib::cInt>::max()
|
||||
#define POINT_MIN std::numeric_limits<coord_t>::min()
|
||||
#define POINT_MAX std::numeric_limits<coord_t>::max()
|
||||
|
||||
static Point no_point(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
|
||||
static Point no_point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max());
|
||||
|
||||
/* Extra operators to make it easier to do math with the 64bit Point objects */
|
||||
INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); }
|
||||
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
if (ac_size == 0)
|
||||
{
|
||||
int64_t ab_dist2 = vSize2(ab);
|
||||
if (ab_dist2 == 0)
|
||||
if (ab_dist2 == 0 && b_is_beyond_ac)
|
||||
{
|
||||
*b_is_beyond_ac = 0; // a is on b is on c
|
||||
}
|
||||
|
||||
+41
-68
@@ -8,23 +8,6 @@
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*
|
||||
* Implementation of offset polygon used by PolygonRef and ConstPolygonRef
|
||||
*
|
||||
* \param ret_paths[out] where the offset polygon is stored.
|
||||
* \param path the path to be offset.
|
||||
* \param distance the distance to offset path.
|
||||
* \param joinType See ClipperLib documentation.
|
||||
* \param miter_limit See ClipperLib documentation.
|
||||
*/
|
||||
inline void PolygonRef_offset_impl(ClipperLib::Paths& ret_path, const ClipperLib::Path& path, int distance, ClipperLib::JoinType join_type, double miter_limit)
|
||||
{
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPath(path, join_type, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret_path, distance);
|
||||
}
|
||||
|
||||
bool PolygonRef::shorterThan(int64_t check_length) const
|
||||
{
|
||||
const PolygonRef& polygon = *this;
|
||||
@@ -44,7 +27,7 @@ bool PolygonRef::shorterThan(int64_t check_length) const
|
||||
|
||||
bool PolygonRef::_inside(Point p, bool border_result) const
|
||||
{
|
||||
const PolygonRef& thiss = *this;
|
||||
PolygonRef thiss = *this;
|
||||
if (size() < 1)
|
||||
{
|
||||
return false;
|
||||
@@ -207,6 +190,16 @@ unsigned int Polygons::findInside(Point p, bool border_result)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Polygons Polygons::removeComplexParts() const
|
||||
{
|
||||
Polygons ret;
|
||||
ClipperLib::Clipper clipper(clipper_init);
|
||||
clipper.AddPaths(paths, ClipperLib::ptSubject, true);
|
||||
clipper.Execute(ClipperLib::ctUnion, ret.paths, ClipperLib::pftPositive);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double miter_limit) const
|
||||
{
|
||||
Polygons ret;
|
||||
@@ -220,7 +213,10 @@ Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double m
|
||||
Polygons PolygonRef::offset(int distance, ClipperLib::JoinType joinType, double miter_limit) const
|
||||
{
|
||||
Polygons ret;
|
||||
PolygonRef_offset_impl(ret.paths, *path, distance, joinType, miter_limit);
|
||||
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
|
||||
clipper.AddPath(*path, joinType, ClipperLib::etClosedPolygon);
|
||||
clipper.MiterLimit = miter_limit;
|
||||
clipper.Execute(ret.paths, distance);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -966,7 +962,7 @@ Polygons Polygons::smooth_outward(float max_angle, int shortcut_length)
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void PolygonRef_smooth_impl(const ClipperLib::Path& thiss, int remove_length, ClipperLib::Path* poly)
|
||||
void PolygonRef::smooth(int remove_length, PolygonRef result)
|
||||
{
|
||||
// a typical zigzag with the middle part to be removed by removing (1) :
|
||||
//
|
||||
@@ -981,7 +977,9 @@ inline void PolygonRef_smooth_impl(const ClipperLib::Path& thiss, int remove_len
|
||||
// |
|
||||
// |
|
||||
// 0
|
||||
if (thiss.size() > 0)
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.path;
|
||||
if (size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
}
|
||||
@@ -1015,11 +1013,11 @@ inline void PolygonRef_smooth_impl(const ClipperLib::Path& thiss, int remove_len
|
||||
Point v02T = turn90CCW(v02);
|
||||
int64_t v02_size = vSize(v02);
|
||||
bool force_push = false;
|
||||
for (unsigned int poly_idx = 1; poly_idx < thiss.size(); poly_idx++)
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
const Point& p1 = thiss[poly_idx];
|
||||
const Point& p2 = thiss[(poly_idx + 1) % thiss.size()];
|
||||
const Point& p3 = thiss[(poly_idx + 2) % thiss.size()];
|
||||
const Point& p2 = thiss[(poly_idx + 1) % size()];
|
||||
const Point& p3 = thiss[(poly_idx + 2) % size()];
|
||||
// v02 computed in last iteration
|
||||
// v02_size as well
|
||||
const Point v12 = p2 - p1;
|
||||
@@ -1048,22 +1046,12 @@ inline void PolygonRef_smooth_impl(const ClipperLib::Path& thiss, int remove_len
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth_impl(*path, remove_length, result.path);
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth_impl(*path, remove_length, &(*result));
|
||||
}
|
||||
|
||||
Polygons Polygons::smooth(int remove_length) const
|
||||
Polygons Polygons::smooth(int remove_length)
|
||||
{
|
||||
Polygons ret;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
ConstPolygonRef poly(paths[p]);
|
||||
PolygonRef poly(paths[p]);
|
||||
if (poly.size() < 3)
|
||||
{
|
||||
continue;
|
||||
@@ -1083,21 +1071,23 @@ Polygons Polygons::smooth(int remove_length) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void PolygonRef_smooth2_impl(const ClipperLib::Path& thiss, int remove_length, ClipperLib::Path* poly)
|
||||
void PolygonRef::smooth2(int remove_length, PolygonRef result)
|
||||
{
|
||||
if (thiss.size() > 0)
|
||||
PolygonRef& thiss = *this;
|
||||
ClipperLib::Path* poly = result.path;
|
||||
if (size() > 0)
|
||||
{
|
||||
poly->push_back(thiss[0]);
|
||||
}
|
||||
for (unsigned int poly_idx = 1; poly_idx < thiss.size(); poly_idx++)
|
||||
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
|
||||
{
|
||||
const Point& last = thiss[poly_idx - 1];
|
||||
const Point& now = thiss[poly_idx];
|
||||
const Point& next = thiss[(poly_idx + 1) % thiss.size()];
|
||||
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
|
||||
Point& last = thiss[poly_idx - 1];
|
||||
Point& now = thiss[poly_idx];
|
||||
Point& next = thiss[(poly_idx + 1) % size()];
|
||||
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
|
||||
{
|
||||
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
|
||||
if (poly_idx < thiss.size())
|
||||
if (poly_idx < size())
|
||||
{
|
||||
poly->push_back(thiss[poly_idx]);
|
||||
}
|
||||
@@ -1109,22 +1099,12 @@ inline void PolygonRef_smooth2_impl(const ClipperLib::Path& thiss, int remove_le
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth2_impl(*path, remove_length, result.path);
|
||||
}
|
||||
|
||||
void ConstPolygonRef::smooth2(int remove_length, PolygonRef result) const
|
||||
{
|
||||
PolygonRef_smooth2_impl(*path, remove_length, &(*result));
|
||||
}
|
||||
|
||||
Polygons Polygons::smooth2(int remove_length, int min_area) const
|
||||
Polygons Polygons::smooth2(int remove_length, int min_area)
|
||||
{
|
||||
Polygons ret;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
ConstPolygonRef poly(paths[p]);
|
||||
PolygonRef poly(paths[p]);
|
||||
if (poly.size() == 0)
|
||||
{
|
||||
continue;
|
||||
@@ -1146,13 +1126,6 @@ Polygons Polygons::smooth2(int remove_length, int min_area) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType joinType, double miter_limit) const
|
||||
{
|
||||
Polygons ret;
|
||||
PolygonRef_offset_impl(ret.paths, *path, distance, joinType, miter_limit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<PolygonsPart> Polygons::splitIntoParts(bool unionAll) const
|
||||
{
|
||||
std::vector<PolygonsPart> ret;
|
||||
@@ -1174,10 +1147,10 @@ void Polygons::splitIntoParts_processPolyTreeNode(ClipperLib::PolyNode* node, st
|
||||
{
|
||||
ClipperLib::PolyNode* child = node->Childs[n];
|
||||
PolygonsPart part;
|
||||
part.add(ConstPolygonRef{child->Contour});
|
||||
part.add(child->Contour);
|
||||
for(int i=0; i<child->ChildCount(); i++)
|
||||
{
|
||||
part.add(ConstPolygonRef{child->Childs[i]->Contour});
|
||||
part.add(child->Childs[i]->Contour);
|
||||
splitIntoParts_processPolyTreeNode(child->Childs[i], ret);
|
||||
}
|
||||
ret.push_back(part);
|
||||
@@ -1209,7 +1182,7 @@ PolygonsPart PartsView::assemblePart(unsigned int part_idx) const
|
||||
{
|
||||
for (unsigned int poly_idx_ff : partsView[part_idx])
|
||||
{
|
||||
ret.add(PolygonRef{polygons[poly_idx_ff]});
|
||||
ret.add(polygons[poly_idx_ff]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -1252,11 +1225,11 @@ void Polygons::splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Poly
|
||||
partsView.emplace_back();
|
||||
unsigned int pos = partsView.size() - 1;
|
||||
partsView[pos].push_back(reordered.size());
|
||||
reordered.add(ConstPolygonRef{child->Contour}); //TODO: should this steal the internal representation for speed?
|
||||
reordered.add(child->Contour);
|
||||
for(int i = 0; i < child->ChildCount(); i++)
|
||||
{
|
||||
partsView[pos].push_back(reordered.size());
|
||||
reordered.add(ConstPolygonRef{child->Childs[i]->Contour});
|
||||
reordered.add(child->Childs[i]->Contour);
|
||||
splitIntoPartsView_processPolyTreeNode(partsView, reordered, child->Childs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-227
@@ -43,12 +43,6 @@ class PolygonRef
|
||||
: path(nullptr)
|
||||
{}
|
||||
public:
|
||||
PolygonRef(PolygonRef& polygon)
|
||||
:path{polygon.path}
|
||||
{}
|
||||
PolygonRef(PolygonRef&& polygon)
|
||||
:path{polygon.path}
|
||||
{}
|
||||
PolygonRef(ClipperLib::Path& polygon)
|
||||
: path(&polygon)
|
||||
{}
|
||||
@@ -84,9 +78,7 @@ public:
|
||||
bool operator==(const PolygonRef& other) const =delete;
|
||||
|
||||
ClipperLib::Path& operator*() { return *path; }
|
||||
|
||||
const ClipperLib::Path& operator*() const { return *path; }
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
@@ -198,7 +190,7 @@ public:
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
Point closestPointTo(Point p) const
|
||||
Point closestPointTo(Point p)
|
||||
{
|
||||
Point ret = p;
|
||||
float bestDist = FLT_MAX;
|
||||
@@ -261,7 +253,7 @@ public:
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth(int remove_length, PolygonRef result) const;
|
||||
void smooth(int remove_length, PolygonRef result);
|
||||
|
||||
/*!
|
||||
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
|
||||
@@ -279,7 +271,7 @@ public:
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth2(int remove_length, PolygonRef result) const;
|
||||
void smooth2(int remove_length, PolygonRef result);
|
||||
|
||||
/*!
|
||||
* Removes consecutive line segments with same orientation and changes this polygon.
|
||||
@@ -383,193 +375,6 @@ private:
|
||||
static void smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far);
|
||||
};
|
||||
|
||||
class ConstPolygonRef
|
||||
{
|
||||
const ClipperLib::Path* path;
|
||||
ConstPolygonRef()
|
||||
: path(nullptr)
|
||||
{}
|
||||
public:
|
||||
ConstPolygonRef(const ClipperLib::Path& polygon)
|
||||
: path(&polygon)
|
||||
{}
|
||||
ConstPolygonRef(const PolygonRef& polygon)
|
||||
: path(&(*polygon))
|
||||
{}
|
||||
|
||||
unsigned int size() const
|
||||
{
|
||||
return path->size();
|
||||
}
|
||||
|
||||
const Point& operator[] (unsigned int index) const
|
||||
{
|
||||
POLY_ASSERT(index < size());
|
||||
return (*path)[index];
|
||||
}
|
||||
|
||||
const void* data() const
|
||||
{
|
||||
return path->data();
|
||||
}
|
||||
|
||||
ConstPolygonRef& operator=(const ConstPolygonRef& other) { path = other.path; return *this; }
|
||||
|
||||
ConstPolygonRef& operator=(const PolygonRef& other) { path = &(*other); return *this; }
|
||||
|
||||
bool operator==(const ConstPolygonRef& other) const =delete;
|
||||
|
||||
const ClipperLib::Path& operator*() const { return *path; }
|
||||
|
||||
/*!
|
||||
* On Y-axis positive upward displays, Orientation will return true if the polygon's orientation is counter-clockwise.
|
||||
*
|
||||
* from http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/Orientation.htm
|
||||
*/
|
||||
bool orientation() const
|
||||
{
|
||||
return ClipperLib::Orientation(*path);
|
||||
}
|
||||
|
||||
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const;
|
||||
|
||||
int64_t polygonLength() const
|
||||
{
|
||||
int64_t length = 0;
|
||||
Point p0 = (*path)[path->size()-1];
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
Point p1 = (*path)[n];
|
||||
length += vSize(p0 - p1);
|
||||
p0 = p1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
bool shorterThan(int64_t check_length) const;
|
||||
|
||||
Point min() const
|
||||
{
|
||||
Point ret = Point(POINT_MAX, POINT_MAX);
|
||||
for(Point p : *path)
|
||||
{
|
||||
ret.X = std::min(ret.X, p.X);
|
||||
ret.Y = std::min(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point max() const
|
||||
{
|
||||
Point ret = Point(POINT_MIN, POINT_MIN);
|
||||
for(Point p : *path)
|
||||
{
|
||||
ret.X = std::max(ret.X, p.X);
|
||||
ret.Y = std::max(ret.Y, p.Y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
double area() const
|
||||
{
|
||||
return ClipperLib::Area(*path);
|
||||
}
|
||||
|
||||
Point centerOfMass() const
|
||||
{
|
||||
double x = 0, y = 0;
|
||||
Point p0 = (*path)[path->size()-1];
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
Point p1 = (*path)[n];
|
||||
double second_factor = (p0.X * p1.Y) - (p1.X * p0.Y);
|
||||
|
||||
x += double(p0.X + p1.X) * second_factor;
|
||||
y += double(p0.Y + p1.Y) * second_factor;
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
double area = Area(*path);
|
||||
|
||||
x = x / 6 / area;
|
||||
y = y / 6 / area;
|
||||
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
Point closestPointTo(Point p) const
|
||||
{
|
||||
Point ret = p;
|
||||
float bestDist = FLT_MAX;
|
||||
for(unsigned int n=0; n<path->size(); n++)
|
||||
{
|
||||
float dist = vSize2f(p - (*path)[n]);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
ret = (*path)[n];
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Clipper function.
|
||||
* Returns false if outside, true if inside; if the point lies exactly on the border, will return 'border_result'.
|
||||
*
|
||||
* http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/PointInPolygon.htm
|
||||
*/
|
||||
bool inside(Point p, bool border_result = false) const
|
||||
{
|
||||
int res = ClipperLib::PointInPolygon(p, *path);
|
||||
if (res == -1)
|
||||
{
|
||||
return border_result;
|
||||
}
|
||||
return res == 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Smooth out small perpendicular segments and store the result in \p result.
|
||||
* Smoothing is performed by removing the inner most vertex of a line segment smaller than \p remove_length
|
||||
* which has an angle with the next and previous line segment smaller than roughly 150*
|
||||
*
|
||||
* Note that in its current implementation this function doesn't remove line segments with an angle smaller than 30*
|
||||
* Such would be the case for an N shape.
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth(int remove_length, PolygonRef result) const;
|
||||
|
||||
/*!
|
||||
* Smooth out the polygon and store the result in \p result.
|
||||
* Smoothing is performed by removing vertices for which both connected line segments are smaller than \p remove_length
|
||||
*
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \param result (output) The result polygon, assumed to be empty
|
||||
*/
|
||||
void smooth2(int remove_length, PolygonRef result) const;
|
||||
|
||||
ClipperLib::Path::const_reference back() const
|
||||
{
|
||||
return path->back();
|
||||
}
|
||||
|
||||
ClipperLib::Path::const_iterator begin() const
|
||||
{
|
||||
return path->begin();
|
||||
}
|
||||
|
||||
ClipperLib::Path::const_iterator end() const
|
||||
{
|
||||
return path->end();
|
||||
}
|
||||
|
||||
friend class Polygons;
|
||||
friend class Polygon;
|
||||
};
|
||||
|
||||
class Polygon : public PolygonRef
|
||||
{
|
||||
ClipperLib::Path poly;
|
||||
@@ -592,7 +397,6 @@ class Polygons
|
||||
{
|
||||
friend class Polygon;
|
||||
friend class PolygonRef;
|
||||
friend class ConstPolygonRef;
|
||||
protected:
|
||||
ClipperLib::Paths paths;
|
||||
public:
|
||||
@@ -603,14 +407,14 @@ public:
|
||||
|
||||
unsigned int pointCount() const; //!< Return the amount of points in all polygons
|
||||
|
||||
ClipperLib::Path& operator[] (unsigned int index)
|
||||
PolygonRef operator[] (unsigned int index)
|
||||
{
|
||||
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
|
||||
return paths[index];
|
||||
return PolygonRef(paths[index]);
|
||||
}
|
||||
ConstPolygonRef operator[] (unsigned int index) const
|
||||
const PolygonRef operator[] (unsigned int index) const
|
||||
{
|
||||
return paths[index];
|
||||
return const_cast<Polygons*>(this)->operator[](index);
|
||||
}
|
||||
ClipperLib::Paths::iterator begin()
|
||||
{
|
||||
@@ -657,10 +461,6 @@ public:
|
||||
{
|
||||
paths.push_back(*poly.path);
|
||||
}
|
||||
void add(const ConstPolygonRef& poly)
|
||||
{
|
||||
paths.push_back(*poly.path);
|
||||
}
|
||||
void add(Polygon&& other_poly)
|
||||
{
|
||||
paths.emplace_back(std::move(*other_poly));
|
||||
@@ -684,14 +484,14 @@ public:
|
||||
paths.emplace_back(args...);
|
||||
}
|
||||
|
||||
ClipperLib::Path& newPoly()
|
||||
PolygonRef newPoly()
|
||||
{
|
||||
paths.emplace_back();
|
||||
return paths.back();
|
||||
return PolygonRef(paths.back());
|
||||
}
|
||||
ClipperLib::Path& back()
|
||||
PolygonRef back()
|
||||
{
|
||||
return paths.back();
|
||||
return PolygonRef(paths.back());
|
||||
}
|
||||
|
||||
Polygons() {}
|
||||
@@ -847,7 +647,7 @@ public:
|
||||
* \param remove_length The length of the largest segment removed
|
||||
* \return The smoothed polygon
|
||||
*/
|
||||
Polygons smooth(int remove_length) const;
|
||||
Polygons smooth(int remove_length);
|
||||
|
||||
/*!
|
||||
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
|
||||
@@ -858,7 +658,7 @@ public:
|
||||
*/
|
||||
Polygons smooth_outward(float angle, int shortcut_length);
|
||||
|
||||
Polygons smooth2(int remove_length, int min_area) const; //!< removes points connected to small lines
|
||||
Polygons smooth2(int remove_length, int min_area); //!< removes points connected to small lines
|
||||
|
||||
/*!
|
||||
* removes points connected to similarly oriented lines
|
||||
@@ -873,7 +673,7 @@ public:
|
||||
Polygons& thiss = *this;
|
||||
for (unsigned int p = 0; p < size(); p++)
|
||||
{
|
||||
PolygonRef{thiss[p]}.simplify(smallest_line_segment_squared, allowed_error_distance_squared);
|
||||
thiss[p].simplify(smallest_line_segment_squared, allowed_error_distance_squared);
|
||||
if (thiss[p].size() < 3)
|
||||
{
|
||||
remove(p);
|
||||
@@ -934,7 +734,7 @@ public:
|
||||
Polygons& thiss = *this;
|
||||
for(unsigned int i=0; i<size(); i++)
|
||||
{
|
||||
double area = INT2MM(INT2MM(fabs(PolygonRef{thiss[i]}.area())));
|
||||
double area = INT2MM(INT2MM(fabs(thiss[i].area())));
|
||||
if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
|
||||
{
|
||||
remove(i);
|
||||
@@ -998,16 +798,16 @@ public:
|
||||
* Removes the same polygons from this set (and also empty polygons).
|
||||
* Polygons are considered the same if all points lie within [same_distance] of their counterparts.
|
||||
*/
|
||||
Polygons remove(const Polygons& to_be_removed, int same_distance = 0) const
|
||||
Polygons remove(Polygons& to_be_removed, int same_distance = 0)
|
||||
{
|
||||
Polygons result;
|
||||
for (unsigned int poly_keep_idx = 0; poly_keep_idx < size(); poly_keep_idx++)
|
||||
{
|
||||
ConstPolygonRef poly_keep = (*this)[poly_keep_idx];
|
||||
PolygonRef poly_keep = (*this)[poly_keep_idx];
|
||||
bool should_be_removed = false;
|
||||
if (poly_keep.size() > 0)
|
||||
// for (int hole_poly_idx = 0; hole_poly_idx < to_be_removed.size(); hole_poly_idx++)
|
||||
for (ConstPolygonRef poly_rem : to_be_removed)
|
||||
for (PolygonRef poly_rem : to_be_removed)
|
||||
{
|
||||
// PolygonRef poly_rem = to_be_removed[hole_poly_idx];
|
||||
if (poly_rem.size() != poly_keep.size() || poly_rem.size() == 0) continue;
|
||||
@@ -1059,6 +859,18 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Remove holes which are lying outside of parts, and outlines inside of parts
|
||||
*
|
||||
* ^↘
|
||||
* ^ ↘
|
||||
* <<<<<<<<^<<<< should become <<<<<<<<
|
||||
* ^ ^
|
||||
* ^ ^
|
||||
* ^ ^
|
||||
*/
|
||||
Polygons removeComplexParts() const;
|
||||
|
||||
int64_t polygonLength() const
|
||||
{
|
||||
int64_t length = 0;
|
||||
@@ -1123,24 +935,21 @@ public:
|
||||
class PolygonsPart : public Polygons
|
||||
{
|
||||
public:
|
||||
PolygonRef outerPolygon()
|
||||
PolygonRef outerPolygon()
|
||||
{
|
||||
return this->paths[0];
|
||||
}
|
||||
ConstPolygonRef outerPolygon() const
|
||||
{
|
||||
return this->paths[0];
|
||||
Polygons& thiss = *this;
|
||||
return thiss[0];
|
||||
}
|
||||
|
||||
bool inside(Point p)
|
||||
{
|
||||
if (size() < 1)
|
||||
return false;
|
||||
if (!PolygonRef{(*this)[0]}.inside(p))
|
||||
if (!(*this)[0].inside(p))
|
||||
return false;
|
||||
for(unsigned int n=1; n<paths.size(); n++)
|
||||
{
|
||||
if (PolygonRef{(*this)[n]}.inside(p))
|
||||
if ((*this)[n].inside(p))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -22,7 +22,7 @@ int64_t PolygonUtils::segmentLength(PolygonsPointIndex start, PolygonsPointIndex
|
||||
assert(start.poly_idx == end.poly_idx);
|
||||
int64_t segment_length = 0;
|
||||
Point prev_vert = start.p();
|
||||
ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
|
||||
const PolygonRef poly = (*start.polygons)[start.poly_idx];
|
||||
for (unsigned int point_idx = 1; point_idx <= poly.size(); point_idx++)
|
||||
{
|
||||
unsigned int vert_idx = (start.point_idx + point_idx) % poly.size();
|
||||
@@ -44,7 +44,7 @@ void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end,
|
||||
assert(start.poly_idx == end.poly_idx);
|
||||
int64_t segment_length = segmentLength(start, end);
|
||||
|
||||
ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
|
||||
const PolygonRef poly = (*start.polygons)[start.poly_idx];
|
||||
unsigned int n_dots_in_between = n_dots;
|
||||
if (start == end)
|
||||
{
|
||||
@@ -80,7 +80,7 @@ void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end,
|
||||
assert(result.size() == n_dots && "we didn't generate as many wipe locations as we asked for.");
|
||||
}
|
||||
|
||||
Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx)
|
||||
Point PolygonUtils::getVertexInwardNormal(PolygonRef poly, unsigned int point_idx)
|
||||
{
|
||||
Point p1 = poly[point_idx];
|
||||
|
||||
@@ -110,7 +110,7 @@ Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int poi
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Point& p2 = poly[p2_idx];
|
||||
Point& p2 = poly[p2_idx];
|
||||
|
||||
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(10.0))); // 10.0 for some precision
|
||||
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(10.0))); // 10.0 for some precision
|
||||
@@ -119,7 +119,7 @@ Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int poi
|
||||
}
|
||||
|
||||
|
||||
Point PolygonUtils::getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset)
|
||||
Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int point_idx, int64_t offset)
|
||||
{
|
||||
return poly[point_idx] + normal(getVertexInwardNormal(poly, point_idx), -offset);
|
||||
}
|
||||
@@ -130,7 +130,7 @@ Point PolygonUtils::moveInsideDiagonally(ClosestPolygonPoint point_on_boundary,
|
||||
{
|
||||
return no_point;
|
||||
}
|
||||
ConstPolygonRef poly = *point_on_boundary.poly;
|
||||
PolygonRef poly = *point_on_boundary.poly;
|
||||
Point p0 = poly[point_on_boundary.point_idx];
|
||||
Point p1 = poly[(point_on_boundary.point_idx + 1) % poly.size()];
|
||||
if (vSize2(p0 - point_on_boundary.location) < vSize2(p1 - point_on_boundary.location))
|
||||
@@ -163,7 +163,7 @@ ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, Point& f
|
||||
return _moveInside2(*closest_polygon_point, distance, from, max_dist2);
|
||||
}
|
||||
|
||||
ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
|
||||
ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& loc_to_line_polygons, const PolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
|
||||
{
|
||||
std::optional<ClosestPolygonPoint> closest_polygon_point;
|
||||
if (loc_to_line_grid)
|
||||
@@ -225,7 +225,7 @@ unsigned int PolygonUtils::moveInside(const Polygons& polygons, Point& from, int
|
||||
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
ConstPolygonRef poly = polygons[poly_idx];
|
||||
const PolygonRef poly = polygons[poly_idx];
|
||||
if (poly.size() < 2)
|
||||
continue;
|
||||
Point p0 = poly[poly.size()-2];
|
||||
@@ -344,11 +344,11 @@ Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distanc
|
||||
{ // the point which is assumed to be on the boundary doesn't have to be moved
|
||||
return cpp.location;
|
||||
}
|
||||
ConstPolygonRef poly = *cpp.poly;
|
||||
const PolygonRef poly = *cpp.poly;
|
||||
unsigned int point_idx = cpp.point_idx;
|
||||
const Point& on_boundary = cpp.location;
|
||||
|
||||
const Point& p1 = poly[point_idx];
|
||||
Point& p1 = poly[point_idx];
|
||||
unsigned int p2_idx;
|
||||
for (p2_idx = point_idx + 1; p2_idx != point_idx; p2_idx = p2_idx + 1)
|
||||
{ // find the next point different from p1
|
||||
@@ -361,7 +361,7 @@ Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distanc
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Point& p2 = poly[p2_idx];
|
||||
Point& p2 = poly[p2_idx];
|
||||
|
||||
if (on_boundary == p1)
|
||||
{
|
||||
@@ -392,7 +392,7 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons
|
||||
{
|
||||
return ClosestPolygonPoint(); // we couldn't move inside
|
||||
}
|
||||
ConstPolygonRef closest_poly = *closest_polygon_point.poly;
|
||||
PolygonRef closest_poly = *closest_polygon_point.poly;
|
||||
bool is_outside_boundary = closest_poly.orientation();
|
||||
|
||||
{
|
||||
@@ -481,8 +481,8 @@ void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, Clo
|
||||
{
|
||||
return;
|
||||
}
|
||||
ConstPolygonRef poly1 = *poly1_result.poly;
|
||||
ConstPolygonRef poly2 = *poly2_result.poly;
|
||||
PolygonRef poly1 = *poly1_result.poly;
|
||||
PolygonRef poly2 = *poly2_result.poly;
|
||||
if (poly1.size() == 0 || poly2.size() == 0)
|
||||
{
|
||||
return;
|
||||
@@ -515,8 +515,8 @@ void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_re
|
||||
{
|
||||
return;
|
||||
}
|
||||
ConstPolygonRef poly1 = *poly1_result.poly;
|
||||
ConstPolygonRef poly2 = *poly2_result.poly;
|
||||
PolygonRef poly1 = *poly1_result.poly;
|
||||
PolygonRef poly2 = *poly2_result.poly;
|
||||
if (poly1_result.point_idx < 0 || poly2_result.point_idx < 0)
|
||||
{
|
||||
return;
|
||||
@@ -537,7 +537,7 @@ void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_re
|
||||
}
|
||||
}
|
||||
|
||||
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx)
|
||||
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx)
|
||||
{
|
||||
ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1);
|
||||
if (!forth.isValid())
|
||||
@@ -556,7 +556,7 @@ ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef
|
||||
}
|
||||
}
|
||||
|
||||
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx, int direction)
|
||||
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction)
|
||||
{
|
||||
if (polygon.size() == 0)
|
||||
{
|
||||
@@ -572,8 +572,8 @@ ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef
|
||||
{
|
||||
int p1_idx = (polygon.size() + direction*p + start_idx) % polygon.size();
|
||||
int p2_idx = (polygon.size() + direction*(p+1) + start_idx) % polygon.size();
|
||||
const Point& p1 = polygon[p1_idx];
|
||||
const Point& p2 = polygon[p2_idx];
|
||||
Point& p1 = polygon[p1_idx];
|
||||
Point& p2 = polygon[p2_idx];
|
||||
|
||||
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
|
||||
int64_t dist = vSize2(from - closest_here);
|
||||
@@ -600,7 +600,7 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygo
|
||||
{
|
||||
return none;
|
||||
}
|
||||
ConstPolygonRef any_polygon = polygons[0];
|
||||
PolygonRef any_polygon = polygons[0];
|
||||
unsigned int any_poly_idx;
|
||||
for (any_poly_idx = 0; any_poly_idx < polygons.size(); any_poly_idx++)
|
||||
{ // find first point in all polygons
|
||||
@@ -620,7 +620,7 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygo
|
||||
|
||||
for (unsigned int ply = 0; ply < polygons.size(); ply++)
|
||||
{
|
||||
ConstPolygonRef poly = polygons[ply];
|
||||
const PolygonRef poly = polygons[ply];
|
||||
if (poly.size() == 0) continue;
|
||||
ClosestPolygonPoint closest_here = findClosest(from, poly, penalty_function);
|
||||
if (!closest_here.isValid())
|
||||
@@ -639,7 +639,7 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygo
|
||||
return best;
|
||||
}
|
||||
|
||||
ClosestPolygonPoint PolygonUtils::findClosest(Point from, ConstPolygonRef polygon, const std::function<int(Point)>& penalty_function)
|
||||
ClosestPolygonPoint PolygonUtils::findClosest(Point from, const PolygonRef polygon, const std::function<int(Point)>& penalty_function)
|
||||
{
|
||||
if (polygon.size() == 0)
|
||||
{
|
||||
@@ -653,11 +653,11 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, ConstPolygonRef polygo
|
||||
//
|
||||
for (unsigned int p = 0; p<polygon.size(); p++)
|
||||
{
|
||||
const Point& p1 = polygon[p];
|
||||
Point& p1 = polygon[p];
|
||||
|
||||
unsigned int p2_idx = p+1;
|
||||
if (p2_idx >= polygon.size()) p2_idx = 0;
|
||||
const Point& p2 = polygon[p2_idx];
|
||||
Point& p2 = polygon[p2_idx];
|
||||
|
||||
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
|
||||
int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
|
||||
@@ -678,7 +678,7 @@ PolygonsPointIndex PolygonUtils::findNearestVert(const Point from, const Polygon
|
||||
PolygonsPointIndex closest_vert;
|
||||
for (unsigned int poly_idx = 0; poly_idx < polys.size(); poly_idx++)
|
||||
{
|
||||
ConstPolygonRef poly = polys[poly_idx];
|
||||
const PolygonRef poly = polys[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
int64_t dist2 = vSize2(poly[point_idx] - from);
|
||||
@@ -721,7 +721,7 @@ LocToLineGrid* PolygonUtils::createLocToLineGrid(const Polygons& polygons, int s
|
||||
|
||||
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
|
||||
{
|
||||
ConstPolygonRef poly = polygons[poly_idx];
|
||||
const PolygonRef poly = polygons[poly_idx];
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
ret->insert(PolygonsPointIndex(&polygons, poly_idx, point_idx));
|
||||
@@ -752,9 +752,9 @@ std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
|
||||
PolygonsPointIndex best_point_poly_idx(nullptr, NO_INDEX, NO_INDEX);
|
||||
for (PolygonsPointIndex& point_poly_index : near_lines)
|
||||
{
|
||||
ConstPolygonRef poly = polygons[point_poly_index.poly_idx];
|
||||
const Point& p1 = poly[point_poly_index.point_idx];
|
||||
const Point& p2 = poly[(point_poly_index.point_idx + 1) % poly.size()];
|
||||
const PolygonRef poly = polygons[point_poly_index.poly_idx];
|
||||
Point& p1 = poly[point_poly_index.point_idx];
|
||||
Point& p2 = poly[(point_poly_index.point_idx + 1) % poly.size()];
|
||||
|
||||
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
|
||||
int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
|
||||
@@ -778,7 +778,7 @@ std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
|
||||
|
||||
|
||||
std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> PolygonUtils::findClose(
|
||||
ConstPolygonRef from, const Polygons& destination,
|
||||
const PolygonRef from, const Polygons& destination,
|
||||
const LocToLineGrid& destination_loc_to_line,
|
||||
const std::function<int(Point)>& penalty_function)
|
||||
{
|
||||
@@ -817,7 +817,7 @@ std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> PolygonUtils::f
|
||||
|
||||
|
||||
|
||||
bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
|
||||
bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
|
||||
{
|
||||
|
||||
Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()];
|
||||
@@ -825,7 +825,7 @@ bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, ConstPolyg
|
||||
for (unsigned int prev_idx = start_idx; prev_idx < poly.size(); prev_idx++)
|
||||
{
|
||||
int next_idx = (prev_idx + 1 + poly_start_idx) % poly.size(); // last checked segment is between last point in poly and poly[0]...
|
||||
const Point& next_poly_point = poly[next_idx];
|
||||
Point& next_poly_point = poly[next_idx];
|
||||
if ( !shorterThen(next_poly_point - from, dist) )
|
||||
{
|
||||
/*
|
||||
@@ -932,7 +932,7 @@ bool PolygonUtils::polygonCollidesWithLineSegment(const Point from, const Point
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix)
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
|
||||
{
|
||||
Point p0 = transformation_matrix.apply(poly.back());
|
||||
for(Point p1_ : poly)
|
||||
@@ -947,7 +947,7 @@ bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Po
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(PolygonRef poly, const Point& startPoint, const Point& endPoint)
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const PolygonRef poly, Point& startPoint, Point& endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
@@ -958,9 +958,9 @@ bool PolygonUtils::polygonCollidesWithLineSegment(PolygonRef poly, const Point&
|
||||
return PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix);
|
||||
}
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix)
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
|
||||
{
|
||||
for (ConstPolygonRef poly : polys)
|
||||
for (const PolygonRef poly : const_cast<Polygons&>(polys))
|
||||
{
|
||||
if (poly.size() == 0) { continue; }
|
||||
if (PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix))
|
||||
@@ -973,7 +973,7 @@ bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const P
|
||||
}
|
||||
|
||||
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& startPoint, const Point& endPoint)
|
||||
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, Point& startPoint, Point& endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
|
||||
+17
-17
@@ -20,12 +20,12 @@ namespace cura
|
||||
struct ClosestPolygonPoint
|
||||
{
|
||||
Point location; //!< Result location
|
||||
std::optional<ConstPolygonRef> poly; //!< Polygon in which the result was found (or none if no result was found)
|
||||
std::optional<PolygonRef> poly; //!< Polygon in which the result was found (or none if no result was found)
|
||||
unsigned int poly_idx; //!< The index of the polygon in some Polygons where ClosestPolygonPoint::poly can be found
|
||||
unsigned int point_idx; //!< Index to the first point in the polygon of the line segment on which the result was found
|
||||
ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly) : location(p), poly(true, poly), poly_idx(NO_INDEX), point_idx(pos) {};
|
||||
ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly, int poly_idx) : location(p), poly(true, poly), poly_idx(poly_idx), point_idx(pos) {};
|
||||
ClosestPolygonPoint(ConstPolygonRef poly) : poly(true, poly), poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
|
||||
ClosestPolygonPoint(Point p, int pos, PolygonRef poly) : location(p), poly(true, poly), poly_idx(NO_INDEX), point_idx(pos) {};
|
||||
ClosestPolygonPoint(Point p, int pos, PolygonRef poly, int poly_idx) : location(p), poly(true, poly), poly_idx(poly_idx), point_idx(pos) {};
|
||||
ClosestPolygonPoint(PolygonRef poly) : poly(true, poly), poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
|
||||
ClosestPolygonPoint() : poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
|
||||
Point p() const
|
||||
{ // conformity with other classes
|
||||
@@ -53,7 +53,7 @@ struct PolygonsPointIndexSegmentLocator
|
||||
{
|
||||
std::pair<Point, Point> operator()(const PolygonsPointIndex& val) const
|
||||
{
|
||||
ConstPolygonRef poly = (*val.polygons)[val.poly_idx];
|
||||
PolygonRef poly = (*val.polygons)[val.poly_idx];
|
||||
Point start = poly[val.point_idx];
|
||||
unsigned int next_point_idx = (val.point_idx + 1) % poly.size();
|
||||
Point end = poly[next_point_idx];
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
* \param poly The polygon.
|
||||
* \param point_idx The index of the point in the polygon.
|
||||
*/
|
||||
static Point getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx);
|
||||
static Point getVertexInwardNormal(PolygonRef poly, unsigned int point_idx);
|
||||
|
||||
/*!
|
||||
* Get a point from the \p poly with a given \p offset.
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
* \param offset The distance the point has to be moved outward from the polygon.
|
||||
* \return A point at the given distance inward from the point on the boundary polygon.
|
||||
*/
|
||||
static Point getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset);
|
||||
static Point getBoundaryPointWithOffset(PolygonRef poly, unsigned int point_idx, int64_t offset);
|
||||
|
||||
/*!
|
||||
* Move a point away from the boundary by looking at the boundary normal of the nearest vert.
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
|
||||
* \return The point on the polygon closest to \p from
|
||||
*/
|
||||
static ClosestPolygonPoint moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
static ClosestPolygonPoint moveInside2(const Polygons& loc_to_line_polygons, const PolygonRef polygon, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
|
||||
/*!
|
||||
* The opposite of moveInside.
|
||||
@@ -298,7 +298,7 @@ public:
|
||||
* \param start_idx The index of the point in the polygon from which to start looking.
|
||||
* \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from.
|
||||
*/
|
||||
static ClosestPolygonPoint findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx);
|
||||
static ClosestPolygonPoint findNearestClosest(Point from, const PolygonRef polygon, int start_idx);
|
||||
|
||||
/*!
|
||||
* Find the nearest closest point on a polygon from a given index walking in one direction along the polygon.
|
||||
@@ -309,7 +309,7 @@ public:
|
||||
* \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction
|
||||
* \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from.
|
||||
*/
|
||||
static ClosestPolygonPoint findNearestClosest(const Point from, ConstPolygonRef polygon, int start_idx, int direction);
|
||||
static ClosestPolygonPoint findNearestClosest(const Point from, const PolygonRef polygon, int start_idx, int direction);
|
||||
|
||||
/*!
|
||||
* Find the point closest to \p from in all polygons in \p polygons.
|
||||
@@ -327,7 +327,7 @@ public:
|
||||
*
|
||||
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
|
||||
*/
|
||||
static ClosestPolygonPoint findClosest(Point from, ConstPolygonRef polygon, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
static ClosestPolygonPoint findClosest(Point from, const PolygonRef polygon, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
|
||||
/*!
|
||||
* Find the nearest vertex to \p from in \p polys
|
||||
@@ -382,7 +382,7 @@ public:
|
||||
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
|
||||
* \return A collection of near crossing from the \p from polygon to the \p destination polygon. Each element in the sollection is a pair with as first a cpp in the \p from polygon and as second a cpp in the \p destination polygon.
|
||||
*/
|
||||
static std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findClose(ConstPolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
static std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findClose(const PolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with polygons as given in a loc_to_line grid.
|
||||
@@ -409,7 +409,7 @@ public:
|
||||
* \param start_idx the index of the prev poly point on the poly.
|
||||
* \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result.
|
||||
*/
|
||||
static bool getNextPointWithDistance(Point from, int64_t dist, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
|
||||
static bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
|
||||
|
||||
|
||||
|
||||
@@ -433,7 +433,7 @@ public:
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
static bool polygonCollidesWithLineSegment(const PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
@@ -449,7 +449,7 @@ public:
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithLineSegment(const PolygonRef poly, const Point& startPoint, const Point& endPoint);
|
||||
static bool polygonCollidesWithLineSegment(const PolygonRef poly, Point& startPoint, Point& endPoint);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
@@ -471,7 +471,7 @@ public:
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithLineSegment(const Polygons& polys, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
static bool polygonCollidesWithLineSegment(const Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
|
||||
|
||||
/*!
|
||||
* Checks whether a given line segment collides with a given polygon(s).
|
||||
@@ -487,7 +487,7 @@ public:
|
||||
* \return whether the line segment collides with the boundary of the
|
||||
* polygon(s)
|
||||
*/
|
||||
static bool polygonCollidesWithLineSegment(const Polygons& polys, const Point& startPoint, const Point& endPoint);
|
||||
static bool polygonCollidesWithLineSegment(const Polygons& polys, Point& startPoint, Point& endPoint);
|
||||
|
||||
private:
|
||||
/*!
|
||||
|
||||
+10
-1
@@ -17,8 +17,17 @@ WallOverlapComputation::WallOverlapComputation(Polygons& polygons, int line_widt
|
||||
|
||||
}
|
||||
|
||||
float WallOverlapComputation::getFlow(const Polygons& from, unsigned int poly_idx, unsigned int from_point_idx, unsigned int to_point_idx)
|
||||
{
|
||||
assert(poly_idx < from.size());
|
||||
PolygonRef poly = from[poly_idx];
|
||||
assert(from_point_idx < poly.size());
|
||||
assert(to_point_idx < poly.size());
|
||||
return getFlow(poly[from_point_idx], poly[to_point_idx]);
|
||||
}
|
||||
|
||||
float WallOverlapComputation::getFlow(const Point& from, const Point& to)
|
||||
|
||||
float WallOverlapComputation::getFlow(Point& from, Point& to)
|
||||
{
|
||||
using Point2LinkIt = PolygonProximityLinker::Point2Link::iterator;
|
||||
|
||||
|
||||
+11
-2
@@ -17,6 +17,8 @@
|
||||
#include "utils/ProximityPointLink.h"
|
||||
#include "utils/PolygonProximityLinker.h"
|
||||
|
||||
#include "PolygonFlowAdjuster.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
@@ -44,7 +46,7 @@ namespace cura
|
||||
* The main functionality of this class is performed by the constructor, by calling the constructor of PolygonProximityLinker.
|
||||
* The adjustment during gcode generation is made with the help of WallOverlapComputation::getFlow
|
||||
*/
|
||||
class WallOverlapComputation
|
||||
class WallOverlapComputation : public PolygonFlowAdjuster
|
||||
{
|
||||
PolygonProximityLinker overlap_linker;
|
||||
int64_t line_width;
|
||||
@@ -60,7 +62,14 @@ public:
|
||||
* \param to The ending of the line segment
|
||||
* \return a value between zero and one representing the reduced flow of the line segment
|
||||
*/
|
||||
float getFlow(const Point& from, const Point& to);
|
||||
float getFlow(Point& from, Point& to);
|
||||
|
||||
/*!
|
||||
* \see \ref WallOverlapComputation::getFlow(Point&,Point&)
|
||||
*
|
||||
* \see \ref PolygonFlowAdjuster::getFlow
|
||||
*/
|
||||
float getFlow(const Polygons& from, unsigned int poly_idx, unsigned int from_point_idx, unsigned int to_point_idx);
|
||||
|
||||
/*!
|
||||
* Computes the neccesary priliminaries in order to efficiently compute the flow when generatign gcode paths.
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário