Comparar commits

..

310 Commits

Autor SHA1 Mensagem Data
Tim Kuipers 89a8fd8b4f fix: handle the case where no texture data is provided for haft the faces (CURA-1371) 2017-01-04 17:40:52 +01:00
Tim Kuipers c73cf897ac fix: pixel inversion was 1 off (CURA-1371) 2017-01-04 16:29:02 +01:00
Tim Kuipers 43408040c7 fix: Material::getColor returns zero if no texture was ever loaded (CURA-1371) 2017-01-03 17:23:12 +01:00
Tim Kuipers 0cd3c7829b fix: make ListPolyIt conversion functions use a bool remove_duplicates parameter (CURA-1371) 2017-01-03 10:52:01 +01:00
Tim Kuipers 05c4f3da01 fix: rounding robustness for flow compensation in fuzzy walls (CURA-1371) 2017-01-03 10:30:55 +01:00
Tim Kuipers 44631f3a6f fix: use FuzzyWalls flow computation instead of WallOverlapComputation (CURA-1371) 2016-12-23 14:45:01 +01:00
Tim Kuipers 1ca622b048 refactor: make WallOverlapComputation and FuzzyWalls adhere to the same interface (CURA-1371) 2016-12-23 14:43:46 +01:00
Tim Kuipers 17e02edff7 fix: fixed fuzzy walls and handle corners (CURA-1371)
without handling corners a texture which is black everywhere would result in lower poly outer walls

this is a rewrite of fuzzy skin; a lot of other bugs have also been fixed, such as the computation of the dist_left_over when p0p1 is too short
2016-12-23 12:49:08 +01:00
Tim Kuipers 563317f47d fix: fuzzy skin dist_left_over edge case was wrong (CURA-1371) 2016-12-22 12:16:46 +01:00
Tim Kuipers 4b3a4f57a1 refactor: factor out makeSegmentFuzzy (CURA-1371) 2016-12-22 12:09:59 +01:00
Tim Kuipers 0f15b885f2 refactor: move part of fuzzy walls to constructor (CURA-1371) 2016-12-22 11:49:28 +01:00
Tim Kuipers ecceb71fdd refactor: move fuzzy skin into it's own class and process in FffGcodeWriter (CURA-1371) 2016-12-22 11:39:41 +01:00
Tim Kuipers b8d019ba4a fix: all getSetting should be const (CURA-1371) 2016-12-22 11:22:06 +01:00
Tim Kuipers 310eba6e16 fix: don't fill gaps between fuzzy wall and second wall (CURA-1371) 2016-12-22 10:47:48 +01:00
Tim Kuipers 82c8ac78a5 feat: specify which colors from the texture to use (CURA-1371) 2016-12-22 10:43:46 +01:00
Tim Kuipers 60e5b6088e fix: LinearAlg2D::getDist2FromLineSegment always set optional result even when it's a nullptr (CURA-1371) 2016-12-21 15:57:47 +01:00
Tim Kuipers f8d17c09bf fix: fuzz map used LinearAlg2D::getDist2FromLineSegment wrong (CURA-1371) 2016-12-21 15:57:13 +01:00
Tim Kuipers 72605c9d51 lil doc (CURA-1371) 2016-12-21 14:23:06 +01:00
Tim Kuipers dad342fefd fix/refactor: don't apply offset where no texture is (found) (CURA-1371)
also factors out getCornerOffset from processBumpMap and getCornerDisregard
2016-12-21 14:22:53 +01:00
Tim Kuipers 5b0c3fd4aa feat: bump map face angle correction for dual color dithering (CURA-1371) 2016-12-21 13:54:11 +01:00
Tim Kuipers 7693630f5f lil optimizition: vector.reserve(layer_count) (CURA-1371) 2016-12-21 13:53:10 +01:00
Tim Kuipers e4a23e2bef refactor: add SliceSegment faceIndex info before calling project2D (CURA-1371)
this way it passes the info to the face segment that is passed down into the bump map processor
2016-12-21 13:51:43 +01:00
Tim Kuipers f5e3cf4c91 feat: FaceNormalStorage for storing normal vector info on each face of a mesh (CURA-1371) 2016-12-21 13:12:45 +01:00
Tim Kuipers 22013f70ab feat: Point3::normal analoguous with Point::normal (CURA-1371) 2016-12-20 17:51:04 +01:00
Tim Kuipers 7cf19f06ec FIX: no_point was (0,0,0) while that was not the intent (CURA-1371)
for integer types numeric_limits::infinity evaluates to 0
2016-12-20 17:50:54 +01:00
Tim Kuipers 314ee7ab2d FIX: Point3 used int32_t instead of int64_t (CURA-1371) 2016-12-20 17:49:44 +01:00
Tim Kuipers 16d67fcd3e refactor: give TextureBumpMapProcessor access to the TexturedMesh (CURA-1371) 2016-12-20 13:54:54 +01:00
Tim Kuipers 508a093e26 fix: don't wrap texture coords (1,1) (CURA-1371) 2016-12-20 13:43:54 +01:00
Tim Kuipers 97660d4c80 refactor: move image loading into Material class (CURA-1371) 2016-12-20 12:41:04 +01:00
Tim Kuipers 1f4640e4c2 fix: transfer ownership of pointers when moving a SliceMeshStorage (CURA-1371) 2016-12-20 12:07:18 +01:00
Tim Kuipers b7b9f15063 feat: texture to fuzzy skin amplitude (CURA-1371) 2016-12-20 12:06:35 +01:00
Tim Kuipers 97432cf9f0 fix: image data was upside down (CURA-1371) 2016-12-20 11:18:58 +01:00
Tim Kuipers c081f80d26 fix: only delete image data when last copy of image is used (CURA-1371)
This could probably also have been solved by defining a move constructor which takes responsivility of the data pointed to by the old Material
and a copy constructor which actually copies the data.
2016-12-20 11:18:36 +01:00
Tim Kuipers 4307d39cc9 feat: texture processor to be used for texture_fuzz_map (CURA-1371) 2016-12-20 11:16:36 +01:00
Tim Kuipers 61f1b7d3ba fix: don't go to no-color-offset for gap closer outline segments (CURA-1371) 2016-12-19 15:10:31 +01:00
Tim Kuipers f4118da3e3 fix: texture preprocessing simplified polygons (CURA-1371)
the simplification caused some texture segments not to be found, because it was looking for a single texture segment for a polygon segment which was simplified from multiple face slice segments
2016-12-19 14:45:54 +01:00
Tim Kuipers dba466a029 lil: optimized out a call to abs() (CURA-1371) 2016-12-19 13:06:18 +01:00
Tim Kuipers 019711e24b fix: top layer of each face wasn't sliced (CURA-1371)
this bug was introduced by 1fa6298455
2016-12-19 12:50:12 +01:00
Tim Kuipers 043110db69 fix: handle bump map corner cases better (CURA-1371)
outward corners with positive offset introduce an extra point outside (similar to a miter limit)
outward corners with negative offset don't introduce bump mapping near corner, which would be removed any way.
2016-12-19 10:37:24 +01:00
Tim Kuipers b53295ffd2 fix: wrap around texture coordinates to within [0,1] (CURA-1371)
Does this mean some face textures are wrapped around the wrong way?
2016-12-16 18:52:51 +01:00
Tim Kuipers 319ce6fcfd fixes for stbi image (CURA-1371)
destroy data when done
load implementation of stb only once (Why is this neccessary?!)
without the above I couldn't include the library from two files :S
2016-12-16 18:50:31 +01:00
Tim Kuipers 9d76bb3b3a feat: for each over SparseGrid (CURA-1371) 2016-12-16 18:47:56 +01:00
Tim Kuipers 4bc944ebf6 lil: source for stb lib (CURA-1371) 2016-12-16 14:30:50 +01:00
Tim Kuipers d331687392 Merge branch 'master' into feature_textured_obj_rebased_after_refactor 2016-12-15 18:54:58 +01:00
Tim Kuipers 8a517de1d1 fix: alternate extruder by supplying two meshes in the frontend (CURA-1371)
we need to pass different settings to each extruder, and the frontend controls these settings
when supplying per object settings, derived per object settings might depend on extruder values, so we need to compute per object settings for both colors from the frontend.

alternate_carve_order doesn't do the job, because carving is done after the bump map offsets
2016-12-15 18:35:03 +01:00
Tim Kuipers bcd3347862 feat: bump map alternate with each layer (CURA-1371) 2016-12-15 17:53:18 +01:00
Tim Kuipers aa8e928f0c fix: bump map settings (CURA-1371)
bump_map_enabled, bump_map_point_dist, bump_map_amplitude, bump_map_offset
2016-12-15 17:18:39 +01:00
Tim Kuipers 4529164cdf fix: create SlicerLayer::texture_bump_map in time (CURA-1371) 2016-12-15 16:30:23 +01:00
Tim Kuipers b4140d7772 optimization: efficient material segment lookup (CURA-1371) 2016-12-15 15:54:45 +01:00
Tim Kuipers a9809f3581 refactor: encapsulate registerTextureFaceSlice() in TextureBumpMapProcessor (CURA-1371)
this way I can make segment_to_material_segment an implementation detail of the TextureBumpMapProcessor class.
It is soon te be replaced with a SparseGrid...
2016-12-15 14:59:31 +01:00
Tim Kuipers 2cc01756a0 refactor: moved getColor to MatCoord; removed TexturedMesh parameter to processBumpMap (CURA-1371)
this simplifies a lot of the texture processing
2016-12-15 14:52:33 +01:00
Tim Kuipers a5e9659676 refactor: dynamic_cast rather than virtual methods for TexturedMesh (CURA-1371) 2016-12-15 14:38:05 +01:00
Tim Kuipers 1cf75345ba refactor: move segment_to_material_segment into TextureBumpMapProcessor (CURA-1371) 2016-12-15 13:35:31 +01:00
Tim Kuipers a771600dec refactor: TextureProcessor ==> TextureBumpMapProcessor (CURA-1371) 2016-12-15 13:15:02 +01:00
Tim Kuipers 15df20fe99 REFACTOR: moved all texture related files into subfolder (CURA-1371) 2016-12-14 18:26:29 +01:00
Tim Kuipers 4179178756 lil safety: exit on failed img load; disregard more obj bs (CURA-1371) 2016-12-14 18:13:38 +01:00
Tim Kuipers a3b85c0362 fix: remove polygon complexities introduced by texture offsets (CURA-1371) 2016-12-14 18:02:21 +01:00
Tim Kuipers 0084d16c98 fix: TextureProcessor::dist_left_over wasn't computed properly if p0p1 was too short (CURA-1371)
this resulted in out-of-image texture coordinates

refactor: factor out processSegmentBumpMap out of processBumpMap (CURA-1371)

for readibility.
2016-12-14 17:17:24 +01:00
Tim Kuipers 9ede9ea524 fix: Material: round to nearest pixel (CURA-1371)
also some refactor has been done which is handy for (linear) interpolation, but I don't know whether I should even do this / give the option for it.
2016-12-14 17:06:19 +01:00
Ghostkeeper 05be030c45 Document range check for pre-computed cubes
Contributes to issue CURA-3137.
2016-12-14 15:15:20 +01:00
Tim Kuipers e4d2161de4 lil refactor: inlined Material::getPixelCoords (CURA-1371) 2016-12-14 15:06:37 +01:00
Tim Kuipers 0a824aac3c fix: account for snapping of slice segments for texture retrieval (CURA-1371) 2016-12-14 14:57:30 +01:00
Tim Kuipers cbcd5df2f7 feat: ColoutUsage setting enum (CURA-1371) 2016-12-14 14:55:59 +01:00
Tim Kuipers 31493500f1 debug: output image to std::cerr (CURA-1371) 2016-12-14 14:34:29 +01:00
Ghostkeeper 3c5e745f83 Reintroduce assert for coordinates exceeding maximum int
Because we might still test with models of 2km size.

Contributes to issue CURA-3137.
2016-12-14 14:09:16 +01:00
Tim Kuipers c3689e0b58 feat: more color options in Material (CURA-1371) 2016-12-14 13:34:05 +01:00
Tim Kuipers d3e455290a fix: use image loader library instead of buggy readBMP function (CURA-1371) 2016-12-14 12:43:37 +01:00
Ghostkeeper 2e6cd36f20 Make unused parameters unnamed
This way they cannot be used any more.

Shuts the compiler up for issue CURA-3137.
2016-12-14 12:02:00 +01:00
Ghostkeeper 9fc4a427cd Removed checks of unsigned ints being >= 0
Because that's always true. Even the compiler says that.

Contributes sorta to issue CURA-3137.
2016-12-14 12:00:39 +01:00
Ghostkeeper 5729908023 Remove unused path parameter
According to the compiler, the path parameter of handleChildren was not unused, but that was only because it went recursively to subcalls to handleChildren, so it was actually also unused.

Contributes to issue CURA-3137.
2016-12-14 11:55:40 +01:00
Ghostkeeper c5b90b0ad9 Put check for empty extruder trains before size check
Could save a miniscule amount of time.

Contributes to issue CURA-3137.
2016-12-14 11:50:45 +01:00
Ghostkeeper 382343e558 Make recursion depth unsigned
It can't be negative anyway. And it helps with the signed-unsigned comparison checks.

Contributes to issue CURA-3137.
2016-12-14 11:48:46 +01:00
Ghostkeeper 68b293b880 Make subdivided cubic infill robust to 0% infill
Otherwise it would crash or loop forever.

Contributes to issue CURA-3137.
2016-12-14 11:44:34 +01:00
Ghostkeeper f867c0f53a Prevent crashing when given no extruder trains
Instead we return -1. A bit more robust.

Contributes to issue CURA-3137.
2016-12-14 11:05:05 +01:00
Ghostkeeper ee7e83d138 Merge branch 'mb-layer-height-comment' of https://github.com/smartavionics/CuraEngine into smartavionics-mb-layer-height-comment 2016-12-14 09:29:53 +01:00
Ghostkeeper 50df40c6c6 Revert "Re-use extruder train variable"
This reverts commit c9217d4738.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 4d924fd33d Revert "Add park distance information to retraction config"
This reverts commit 23ef513cce.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper c953a726cb Revert "Add function to move filament to park position"
This reverts commit ae00cbe497.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 7c8c0b2417 Revert "Correct speed of retraction"
This reverts commit 6925f39426.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper a834754d64 Revert "Remove some annoying compile warnings"
This reverts commit d75882a707.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 9957a0c733 Revert "Fix updating current E value"
This reverts commit 80f8760f68.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper f6ce0b4141 Revert "Put filament in parking position when we never use it any more"
This reverts commit 3cb3e5de45.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 120a9c440c Revert "Park filament even if printer doesn't support retraction"
This reverts commit 837f992f69.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Ghostkeeper 59f72bdd98 Revert "Expand documentation of writePark"
This reverts commit 4f3b4f429d.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Ghostkeeper abc43302ac Revert "Correct retraction speed"
This reverts commit 36eb3471df.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Mark Burton c2725bdf83 Move layer_height out of the GCodeExport class - it doesn't need to be a member. 2016-12-13 13:47:39 +00:00
Tim Kuipers 66ad67693e fix: obj loading ignores normals and empty lines (CURA-1371) 2016-12-13 14:21:21 +01:00
Tim Kuipers 43d4097556 refactor: process ==> processBumpMap (CURA-1371) 2016-12-13 14:20:31 +01:00
Tim Kuipers 1d030b4251 lil fix 2016-12-13 14:18:24 +01:00
Tim Kuipers d74fdf2f6c fix: added corner point handling to mesh texture offset (CURA-1371) 2016-12-13 14:18:24 +01:00
Tim Kuipers a4dc1733e8 fix: material coordinates were out of bounds (inverted) (CURA-1371) 2016-12-13 14:18:24 +01:00
Tim Kuipers a6fad000e4 fix: getFaceEdgeMatCoord converted to MM implicitly (CURA-1371) 2016-12-13 14:18:24 +01:00
Tim Kuipers 0274330a09 feat: actual texture to offset computation (still buggy) (CURA-1371)
not all material coordinates seem to be recorded
material coordinates seem always around the same value
2016-12-13 14:18:04 +01:00
Tim Kuipers 119c7e6a3d fix: ambiguous overload in FPoint (CURA-1371) 2016-12-13 13:59:53 +01:00
Tim Kuipers e74ee27256 lil (CURA-1371) 2016-12-13 13:59:53 +01:00
Tim Kuipers 4288a75d0f feat: TextureProcessor for polygons (CURA-1371) 2016-12-13 13:58:58 +01:00
Tim Kuipers fb761dfd9d fix: don't generate perimeter_gaps between inner wall and infill/skin when there is no infill (CURA-3108) 2016-12-13 13:53:05 +01:00
Tim Kuipers ada75a04a0 fix: SlicerSegment hashing fix (CURA-1371) 2016-12-13 11:57:22 +01:00
Tim Kuipers f9ec748e91 feat: segment to material mapping (CURA-1371) 2016-12-13 11:57:22 +01:00
Tim Kuipers 3b59c79bf4 fix: obj parsing fixes and output (CURA-1371) 2016-12-13 11:50:04 +01:00
Tim Kuipers 2cb637b79b lil: SlicerSegment operator== (CURA-1371) 2016-12-13 11:50:04 +01:00
Tim Kuipers dff6595f27 refactor: registerFaceSlice result in MatSegment (CURA-1371) 2016-12-13 11:49:22 +01:00
Tim Kuipers aac3975e5d refactor: factored out child classes from TexturedMesh into MatCoord and FPoint (CURA-1371) 2016-12-13 11:39:02 +01:00
Tim Kuipers f69d070dd4 refactor: factored out child classes from TexturedMesh into MatCoord and FPoint (CURA-1371) 2016-12-13 11:37:48 +01:00
Tim Kuipers d28798ee52 fix: getFaceEdgeMatCoord should result in MatCoord (CURA-1371) 2016-12-13 11:37:48 +01:00
Tim Kuipers 1760d6b8ed fix: obj texture coordinates didn't get loaded (CURA-1371) 2016-12-13 11:37:48 +01:00
Tim Kuipers 80b3436075 refactor: moved slicer related files into slicer folder (CURA-1371) 2016-12-13 11:32:55 +01:00
Tim Kuipers 8ca0fc4cb0 refactor: moved slicer classes to separate files (CURA-1371) 2016-12-13 11:24:43 +01:00
Tim Kuipers 53c4fd28bf feat: mesh.registerFaceSlice for textured meshes (CURA-1371) 2016-12-13 10:40:07 +01:00
Mark Burton 90727a0578 Merge branch 'master' into mb-layer-height-comment 2016-12-13 08:05:31 +00:00
Tim Kuipers 56e8f8b10c refactor: send order information to project2D (slicer) (CURA-1371) 2016-12-12 19:16:37 +01:00
Tim Kuipers 1fa6298455 feat: small optimization in slicer (CURA-1371) 2016-12-12 18:53:59 +01:00
Tim Kuipers e7e0fe4050 feat: TexturedMesh.getFaceEdgeMatCoord (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers a7e0d49235 quick hack: sdfsdsgdgsda 2016-12-12 18:00:20 +01:00
Tim Kuipers 8e85c00452 refactor: material coords double ==> float; feat: TexturedMesh.getMatCoord (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 96a4fe1725 fix: lil int casting thing (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 13fe966db3 feat: retrieve color from material (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 2da5b4960a fix: use updated material in obj file (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 00d87b089b feat: load material image (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 9ec30d8168 feat: loading of mtl (obj) (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers cb21c325f6 fix: support obj relative indexing (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 35c2d018a5 fix: made obj loading robust against not having texture data (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers a9f749b4de fix: load obj file into a TexturedMesh (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 4cecf7b8e3 fix: TexturedMesh contructor (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers d661a31041 fix: delete mesh if it didn't load properly (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers f65b270d2a fix: TexturedMesh.addFace wasn't finished yet (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers de85a67f28 feat: let mesh.addFace return whether it actually inserted a face (CURA-1371) 2016-12-12 18:00:20 +01:00
Tim Kuipers 65e9da693d feat: preliminary work for textured meshes (CURA-1371)
basic file structure and functions
2016-12-12 18:00:20 +01:00
Tim Kuipers 921333a7bc refactor: meshgroup.meshes became a vector of pointers (CURA-1371)
This to support future objects inheriting from Mesh
2016-12-12 17:59:26 +01:00
Mark Burton fd7d1a4bd4 Add comment that reports layer height in RepRap flavour gcode. 2016-12-12 16:54:13 +00:00
Tim Kuipers bb9aa2280d feat: support basic obj files (non-textured) (CURA-1371) 2016-12-12 17:38:39 +01:00
Tim Kuipers a38d2fa31e added functionality to Mesh to load obj files 2016-12-12 17:38:39 +01:00
Ghostkeeper 2b1266c647 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 14:04:32 +01:00
Ghostkeeper 901bf47610 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 13:55:47 +01:00
Ghostkeeper 80a6115537 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 13:44:23 +01:00
Ghostkeeper f94ca645bd Make writeRetraction accept const ref instead of const pointer
This change was already made for CURA-2795, and upon this change was built by other commits for other issues. I had to revert CURA-2795, so that would undo the changes that the other commits depend on. Instead though, I un-reverted the change because it's nice and doesn't have anything inherently to do with CURA-2795.

Contributes to issue CURA-2795.
2016-12-12 13:11:03 +01:00
Ghostkeeper 2067644d30 Revert "Update prime speed corresponding to park retraction"
This reverts commit 6c986e6cfe.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 0285e2f025 Revert "Add additional safety check for extruder switching off in later layers"
This reverts commit bc82fd98c9.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 38a1ee4270 Revert "Allow going to stand-by temperature if nozzle is later used"
This reverts commit 3c0d3f1b2d.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 63459d5cd4 Revert "Correct documentation on why the temperature is switched off later"
This reverts commit 6972101d7e.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 138691436e Revert "Correct technicality in documentation"
This reverts commit ab2c2eed4b.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper b9c5b4593b Revert "Remove unused parameter"
This reverts commit b7e9f72023.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper a0a3a24dc1 Revert "Restore park distance exception for BFB g-code"
This reverts commit b6355b69e7.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5959b41132 Revert "Use filament park distance setting instead of direct heat & cool zones"
This reverts commit b0487fa4c7.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 3d7229c9f2 Revert "Turn extruder temperature off only if it parked the filament"
This reverts commit 84d3381be6.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper e1cfc3d93b Revert "Break retraction in two"
This reverts commit a0625aa735.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5834540bec Revert "Rename writeRetractionMove to writeMoveFilament"
This reverts commit 7c39b6b26a.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper f191d23e17 Revert "Pass retraction configs by reference instead of pointer"
This reverts commit 7a8be50b8f.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5114ab4218 Revert "Use writeMoveFilament to retract to parking distance"
This reverts commit cb7b7bf22a.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 9f5645ecac Revert "Protect writeMoveFilament"
This reverts commit 52220ccab8.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 66befe5827 Revert "Correct documentation of extruder_switch parameter"
This reverts commit 4263801d16.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper c9de64f946 Revert "Use optional to denote uninitiated previous extruder temperature"
This reverts commit 460c52ea6c.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper dbfa3a0f4b Revert "Move park distance data to ExtruderTrainAttributes"
This reverts commit 7777668b86.

Contributes to issue CURA-2795.
2016-12-12 12:54:56 +01:00
Ghostkeeper 02268eb7e8 Revert "Park via E-moves instead of via a retraction"
This reverts commit 6350f47cd6.

Contributes to issue CURA-2795.
2016-12-12 12:54:56 +01:00
Tim Kuipers d2b8c8bf17 Merge pull request #417 from smartavionics/mb-fix-total-filament-report
Reinstate filament used comment for RepRap flavour gcode.
2016-12-12 11:32:58 +01:00
Tim Kuipers d00f5efa77 lil fix: assert used unset extruder_count 2016-12-11 23:12:20 +01:00
Tim Kuipers b8d7162daf lil cast warning fix 2016-12-11 11:56:51 +01:00
Tim Kuipers b0293e2d6a refactor: processSkin ==> processSkinAndPerimeterGaps (CURA-3108) 2016-12-11 11:50:07 +01:00
Tim Kuipers e998eb8899 fix: perform perimeter gaps rounding safety offset only from one side (CURA-3108)
saves computation time
2016-12-11 11:24:01 +01:00
Tim Kuipers 731a69ff86 fix: perimeter gaps offset wasn't used (CURA-3108) 2016-12-11 11:04:49 +01:00
Tim Kuipers d2d34f89d5 lil doc pf planPrime (CURA-3006) 2016-12-10 14:40:58 +01:00
Tim Kuipers 0ad7bd747c fix: extruder was switched without pre-switch code (CURA-3006) 2016-12-10 14:36:13 +01:00
Tim Kuipers 3ad4dbd62e fix: plan a prime at a specified location (CURA-3006) 2016-12-10 14:26:30 +01:00
Tim Kuipers b5c8f95713 safety: assert in Meshgroup::getExtruderTrain (URA-3006) 2016-12-10 13:57:51 +01:00
Tim Kuipers 1590c18041 refactor: make explicit why only GRIFFIN needs to have priming on the initial layer (CURA-3006) 2016-12-10 13:54:25 +01:00
Tim Kuipers 405e8bdd0e fix: properly determine the starting extruder everywhere (CURA-3006) 2016-12-10 13:14:51 +01:00
Tim Kuipers 06c74c3151 lil refactor: initialize FffGcodeWriter members in initializer list (CURA-3006) 2016-12-10 12:40:24 +01:00
Tim Kuipers 28304d1aa9 refactor: compute starting extruder outside of processStartingCode (CURA-3006) 2016-12-10 12:28:27 +01:00
Tim Kuipers a18d61ccb3 refactor: reduced code duplication in Preheat (CURA-3006) 2016-12-10 12:12:51 +01:00
Ghostkeeper 6350f47cd6 Park via E-moves instead of via a retraction
While parking the filament is essentially a retraction, it should not be called this way and therefore we should make an exception for park moves that they do not allow the printer to decide for itself how far to retract the filament.

Contributes to issue CURA-2795.
2016-12-09 16:04:01 +01:00
Ghostkeeper e25a0d1305 Rename near to nearby
Because 'near' is a built-in keyword in the MSVC compiler. You can't use that as a variable name.

Contributes to issue CURA-1112.
2016-12-09 14:06:37 +01:00
Ghostkeeper 31179cab5c Revert "Use std::next instead of incrementing .begin()"
This reverts commit c85146457e.

Contributes to issue CURA-1112.
2016-12-09 14:03:19 +01:00
Ghostkeeper c85146457e Use std::next instead of incrementing .begin()
Hopefully that is allowed by MSVC. Let's see.

Contributes to issue CURA-1112.
2016-12-09 13:41:27 +01:00
Tim Kuipers f1442ba7a5 fix: also add perimeter gaps between the innermost wall and infill/skin (CURA-3108) 2016-12-08 20:24:33 +01:00
Tim Kuipers 33e50f7b05 fix: also do preheating for layers with unretracted moves only (CURA-3006) 2016-12-08 19:35:59 +01:00
Tim Kuipers 865aacbfb1 fix: always prime nozzles on the first layer (CURA-3006) 2016-12-08 19:35:14 +01:00
Tim Kuipers 2bc7ee5df0 feat: prime all nozzles at the end of the first layer (CURA-3006) 2016-12-08 19:34:50 +01:00
Ghostkeeper 7777668b86 Move park distance data to ExtruderTrainAttributes
It was deemed to be more logical there.

Contributes to issue CURA-2795.
2016-12-08 15:35:00 +01:00
Tim Kuipers 96e8c7a73d lil fix: forward declaration of ExtruderPlan for TimeMaterialEstimates (CURA-3006) 2016-12-08 15:30:19 +01:00
Tim Kuipers 89446b8e32 refactor: factor out GCodePath into its own header file (CURA-3006) 2016-12-08 15:27:07 +01:00
Tim Kuipers b2fa8d3be1 refactor: factor out TimeMaterialEstimates into its own .h and .cpp files (CURA-3006) 2016-12-08 15:21:59 +01:00
Tim Kuipers d8e0e84fcc refactor: factor out NozzleTempInsert into its own header file (CURA-3006) 2016-12-08 15:15:30 +01:00
Tim Kuipers 443c630997 Merge branch 'feature_mesh_order' 2016-12-08 15:06:54 +01:00
Tim Kuipers 9831ef3580 lil fix: allow negative bed temperatures (CURA-2781) 2016-12-08 15:05:07 +01:00
Ghostkeeper 460c52ea6c Use optional to denote uninitiated previous extruder temperature
NaN is also a very common pattern, but std::optional is more explicit.

Contributes to issue CURA-2795.
2016-12-08 14:55:15 +01:00
Tim Kuipers 3d1e79c670 lil comment (CURA-2907) 2016-12-08 11:56:21 +01:00
Ghostkeeper 879b86cab9 Also enforce z-distance from the top
This entails a bit slower slicing. How much slower will be determined by a performance test.

Contributes to issue CURA-2907.
2016-12-08 10:17:37 +01:00
Mark Burton 941b1ff1ac Reinstate filament used comment for RepRap flavour gcode. 2016-12-07 14:55:54 +00:00
Ghostkeeper 4263801d16 Correct documentation of extruder_switch parameter
This parameter is only used for firmware-retracting printers.

Contributes to issue CURA-2795.
2016-12-07 15:44:11 +01:00
Ghostkeeper 52220ccab8 Protect writeMoveFilament
It should be used as a helper function only.

Contributes to issue CURA-2795.
2016-12-07 15:41:53 +01:00
Ghostkeeper cb7b7bf22a Use writeMoveFilament to retract to parking distance
This re-uses a bit of code.

Contributes to issue CURA-2795.
2016-12-07 15:40:03 +01:00
Ghostkeeper 7a8be50b8f Pass retraction configs by reference instead of pointer
This makes things a bit safer. It's a const reference, so it should be very easy.

Contributes to issue CURA-2795.
2016-12-07 15:36:52 +01:00
Ghostkeeper 7c39b6b26a Rename writeRetractionMove to writeMoveFilament
I think that is more indicative of what it can do, since it doesn't only concern retractions.

Contributes to issue CURA-2795.
2016-12-07 15:31:40 +01:00
Ghostkeeper a0625aa735 Break retraction in two
One starting point for normal retractions and one to directly move the filament to a certain position.

Contributes to issue CURA-2795.
2016-12-07 15:29:28 +01:00
Ghostkeeper 84d3381be6 Turn extruder temperature off only if it parked the filament
The two had slightly different conditions if the stand-by temperature was zero.

Contributes to issue CURA-2795.
2016-12-07 12:50:49 +01:00
Ghostkeeper 8b01098dd1 Cache prime tower outside polygon
Saves a few polygon unions, since this is the same for each layer anyway.

Contributes to issue CURA-2684.
2016-12-07 11:54:12 +01:00
Ghostkeeper 1bfe4e74d2 Correct height of prime tower to subtract from support
The prime tower has one additional layer on top of the last extruder switch.

Contributes to issue CURA-2684.
2016-12-07 11:49:11 +01:00
Tim Kuipers bcde7f50ee Revert "fix: don't output print temperature in starting code if set to zero (CURA-3101)"
This reverts commit 5db20ee68d.
2016-12-06 16:39:33 +01:00
Tim Kuipers c7cadc132b Revert "fix: don't output print temperature in starting code if set to zero (CURA-3101)"
This reverts commit abfb41006b.
2016-12-06 16:39:12 +01:00
Tim Kuipers abfb41006b fix: don't output print temperature in starting code if set to zero (CURA-3101) 2016-12-06 16:37:35 +01:00
Tim Kuipers 5db20ee68d fix: don't output print temperature in starting code if set to zero (CURA-3101) 2016-12-06 16:36:32 +01:00
Ghostkeeper b0487fa4c7 Use filament park distance setting instead of direct heat & cool zones
The sum of the heat and cool zone lengths is now computed via inheritance in the front-end.

Contributes to issue CURA-2795.
2016-12-06 15:57:39 +01:00
Ghostkeeper b6355b69e7 Restore park distance exception for BFB g-code
Turns out that BFB flavour doesn't have an e-axis, but it still doesn't count as a firmware retract.

Contributes to issue CURA-2795.
2016-12-06 14:58:26 +01:00
Tim Kuipers 9597ee5645 fix: concentric infill always acted as if alternate_extra_perimeter was enabled (CURA-2772) 2016-12-06 14:09:28 +01:00
Tim Kuipers 5ba1cb674e fix: first walls of concentrid 3D infill didn't get added 2016-12-06 13:55:37 +01:00
Tim Kuipers f336d5ffc0 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-12-06 13:03:19 +01:00
Tim Kuipers 1d0ac8528b fix: only ensure unprimed extruders are primed (CURA-3006) 2016-12-06 13:02:35 +01:00
Tim Kuipers 5c06c48c87 small fix: only perform anti-overhang when there is anti-overhang (CURA-3006, CURA-2077) 2016-12-06 13:01:26 +01:00
Tim Kuipers 837d4e7fba feat: tracking of extruder_prime_is_planned (CURA-3006) 2016-12-06 12:59:50 +01:00
Ghostkeeper 7ded01a110 Remove unused prev_extruder parameter
It doesn't need to know which extruder it came from. Just with which extruder to draw the lines. The new_extruder parameter was renamed because there is no need to disambiguate any more.

Contributes to issue CURA-2684.
2016-12-06 11:20:51 +01:00
Ghostkeeper 3f3b526ebd Remove unused storage parameter
It doesn't need to know any settings and the documentation was outdated.

Contributes to issue CURA-2684.
2016-12-06 11:17:28 +01:00
Ghostkeeper b29607b8a9 Remove unused gcode parameter
The results are stored in the gcodeLayer parameter. Has been this way for a while. This was generating compiler warnings. The documentation was already updated, it seems.

Contributes to issue CURA-2684.
2016-12-06 11:15:30 +01:00
Ghostkeeper e641a25aab Subtract prime tower from support after determining prime tower height
There is a circular dependency between prime tower height and support areas. Prime tower height is determined by the highest extruder switch. The highest extruder switch is dependent on the height of support if support is printed with a different exturder. The height of support is dependent on the support areas and the support areas are dependent on the prime tower height. Previously we resolved this by printing no support in the prime tower area and no prime tower, possibly violating the support horizontal expansion in an edge case. Now it is resolved by printing a prime tower there even if there are no extruder switches.

Contributes to issue CURA-2684.
2016-12-06 11:07:10 +01:00
Ghostkeeper b7e9f72023 Remove unused parameter
One of the many. Seems to me that the documentation is outdated if an essential parameter like this one is unused.

Contributes to issue CURA-2795.
2016-12-06 09:44:18 +01:00
Ghostkeeper ab2c2eed4b Correct technicality in documentation
Contributes to issue CURA-2795.
2016-12-06 09:41:40 +01:00
Ghostkeeper 6972101d7e Correct documentation on why the temperature is switched off later
This old code needs to be after the switch, but it was not clear to me why. I tried to make it clear and failed.

Contributes to issue CURA-2795.
2016-12-06 09:40:02 +01:00
Ghostkeeper 3c0d3f1b2d Allow going to stand-by temperature if nozzle is later used
Contributes to issue CURA-2795.
2016-12-06 09:38:08 +01:00
Ghostkeeper bc82fd98c9 Add additional safety check for extruder switching off in later layers
This is never used, but it can't hurt.

Contributes to issue CURA-2795.
2016-12-06 09:32:34 +01:00
Ghostkeeper 6c986e6cfe Update prime speed corresponding to park retraction
We have some double data here, which could lead to asynchronicity. As is evident in my mistake.

Contributes to issue CURA-2795.
2016-12-06 09:30:57 +01:00
Ghostkeeper 36eb3471df Correct retraction speed
Contributes to issue CURA-2795.
2016-12-06 09:24:33 +01:00
Ghostkeeper 4f3b4f429d Expand documentation of writePark
It wasn't clear what retraction limit this function was not adhering to. It is not adhering to the only limit on retraction we currently have: The number of retractions.

Contributes to issue CURA-2795.
2016-12-06 09:10:34 +01:00
Ghostkeeper 837f992f69 Park filament even if printer doesn't support retraction
Let's hope they do support non-increasing E values.

Contributes to issue CURA-2795.
2016-12-06 09:04:07 +01:00
Tim Kuipers ad76736d26 fix: specify starting extruder when adhesion_type==none (CURA-3006, CURA-759) 2016-12-05 17:01:21 +01:00
Tim Kuipers 3fe5a000f2 fix: set extruder back after priming in the first raft layer (CURA-3006) 2016-12-05 17:00:25 +01:00
Ghostkeeper d5a7e2f62e Add documentation of why this union is necessary
Contributes to issue CURA-3017.
2016-12-05 14:23:41 +01:00
Ghostkeeper 6e5315f754 Revert "Also union layer polygons before combing"
This reverts commit 4aff5a1488.
2016-12-05 14:17:22 +01:00
Ghostkeeper 3cb3e5de45 Put filament in parking position when we never use it any more
It should retract before the nozzle switch, of course, so it can't be retracting at the same time as when it's setting the extruder temperature to 0.

Contributes to issue CURA-2795.
2016-12-05 12:04:12 +01:00
Ghostkeeper 80f8760f68 Fix updating current E value
It should be updated before we use it to write the retraction. Also we should subtract the retraction because it is the amount to retract, not the amount to prime.

Contributes to issue CURA-2795.
2016-12-05 12:02:33 +01:00
Ghostkeeper d75882a707 Remove some annoying compile warnings
They are by far not all, but these remove some of the 'unused variable' warnings.

Contributes to issue CURA-2795.
2016-12-05 11:54:51 +01:00
Ghostkeeper 6925f39426 Correct speed of retraction
It should be in millimetres per minute, not millimetres per second. Also, always use the retraction speed.

Contributes to issue CURA-2795.
2016-12-05 11:34:59 +01:00
Tim Kuipers d6cdfefb58 fix: use start_layers_at_same_position to see with which mesh to start a layer (CURA-1112) 2016-12-03 12:43:43 +01:00
Tim Kuipers dfa23feb31 lil optimization vector.reserve (CURA-1112) 2016-12-03 12:26:12 +01:00
Tim Kuipers b0888e4424 refactor: compute mesh order globally (CURA-1112) 2016-12-03 12:25:53 +01:00
Tim Kuipers fd06e10646 fix: calculateMeshOrder now uses a smart path (CURA-1112)
but doesn't yet use the Z seam setting and starts at an arbitrary point in the smart cycle
2016-12-03 12:14:55 +01:00
Tim Kuipers 6e051e0bdc refactor: let OrderOptimizer contain objects of arbitrary type rather than always pointers (CURA-1112) 2016-12-03 12:11:19 +01:00
Ghostkeeper ae00cbe497 Add function to move filament to park position
If the g-code has no control over the retraction distances via G1, it performs a normal retraction instead.

Contributes to issue CURA-2795.
2016-12-02 16:58:13 +01:00
Ghostkeeper 23ef513cce Add park distance information to retraction config
This is the location of the filament when the extruder is parked.

Contributes to issue CURA-2795.
2016-12-02 16:34:19 +01:00
Tim Kuipers 3aa29d898f feat: orderOptimizer (CURA-1112) 2016-12-02 15:22:31 +01:00
Ghostkeeper c9217d4738 Re-use extruder train variable
I moved the definition of the 'train' variable a bit earlier so it can be re-used a bit more often.

Contributes to issue CURA-2795.
2016-12-02 15:05:51 +01:00
Ghostkeeper 16d4ab923f Make support attach directly to prime tower
No X/Y offset is happening. This makes both support and prime tower sturdier, and prevents having to do an offset of the prime tower so it would make the support a bit faster in theory (but the difference is not noticable).

Contributes to issue CURA-2684.
2016-12-02 12:39:05 +01:00
Ghostkeeper 60d4a3108d Don't print support inside hollow prime towers
It only takes the outside of the prime tower ground polygon.

Contributes to issue CURA-2684.
2016-12-02 12:34:59 +01:00
Tim Kuipers 7ad5eee176 indent change only (CURA-1112) 2016-12-02 11:58:38 +01:00
Tim Kuipers 209e9fa50f refactor: separate out FffGcodeWriter::addMeshPartToGCode (CURA-1112) 2016-12-02 11:58:11 +01:00
Ghostkeeper 4aff5a1488 Also union layer polygons before combing
If two polygons overlap, clipperlib can interpret them as one being the inside polygon with the even odd rule. If they overlap exactly, it produces no output. However if you union the polygons it will properly avoid all objects regardless of whether they overlap with other objects.

Contributes to issue CURA-3017.
2016-12-02 11:21:16 +01:00
Ghostkeeper 4f786d5533 Subtract prime tower area from support
At this point we don't know for certain if there will be a prime tower, but that is impossible since there is a circular dependency between prime tower and support then.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Ghostkeeper b4fd72f92f Re-use layer outlines variable
We already had it stored in a variable and the polygons don't change in between, so we can safely re-use this.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Ghostkeeper df892030c5 Only include prime tower if it is enabled
We always generate a ground polygon now, so we can't rely on the ground polygon being empty if it is disabled due to there being no layer switches.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Tim Kuipers e31a516205 fix: ooze/draft shield now processed again (CURA-3077) 2016-12-01 11:57:13 +01:00
Ghostkeeper c77afe06e2 Generate prime tower polygon at construction
This way we can always request the prime tower ground polygon throughout the entire process. The polygon will be empty if prime tower is disabled.

Contributes to issue CURA-2684.
2016-11-30 17:41:41 +01:00
Ghostkeeper 921ffbe659 Merge branch 'feature_z_seam_location' 2016-11-30 16:34:31 +01:00
Ghostkeeper 96ac0758b9 Don't use sqrt in a constexpr
sqrt doesn't return a constexpr. GCC allows it, but Clang doesn't. Instead we just use a preprocessor definition.

Contributes to issue CURA-2602.
2016-11-30 10:44:19 +01:00
Ghostkeeper dd8639b1f5 Use std::abs instead of abs
The clang compiler doesn't define abs outside of the std library.
2016-11-30 10:27:30 +01:00
Ghostkeeper 2572349938 Remove debug prints
These should never have been committed.

Contributes to issue CURA-3017.
2016-11-30 10:09:09 +01:00
Tim Kuipers ac9a053f5b Revert "lil fix: don't compute overhang on first layer"
This reverts commit bec51eb0bd.
2016-11-29 17:19:15 +01:00
Tim Kuipers bec51eb0bd lil fix: don't compute overhang on first layer 2016-11-29 17:17:35 +01:00
Jaime van Kessel 12ccb3512f Merge branch 'feature_recursive_infill_code_review' of github.com:Ultimaker/CuraEngine 2016-11-29 15:42:02 +01:00
Tim Kuipers 87a3a30bc5 fix: open polylines bug
turned back a mistake which was made in a refactor: fa203bd976
2016-11-29 14:32:01 +01:00
Tim Kuipers 6ae41a5e86 fix: max feedrate is in mm/s in both frontend and firmware (CURA-2955)
I've verified that M203 Sxx is interpreted as mm/s in Marlin, Sprinter and Repetier-Firmware.
Other firmware types we support, like MakerBot, BFB and MACH weren't checked.
2016-11-29 13:15:22 +01:00
Ghostkeeper 8ce016064f Fix typo in documentation
The variable name is _dest_point, it seems.

Contributes to issue CURA-2229.
2016-11-28 17:05:51 +01:00
Ghostkeeper 4e16b4313f Fix typo in assert message
Contributes to issue CURA-3008.
2016-11-28 16:55:24 +01:00
Ghostkeeper 5b493c17dc Union first layer polygons for brim too
For skirt it does this union by taking the convex hull. For brim it did no such thing, so when polygons overlapped it would get complex polygons which didn't work out well.

Contributes to issue CURA-3017.
2016-11-28 16:37:28 +01:00
Tim Kuipers 4b45726ada Revert "feat: PolygonPointer as distinct from PolygonRef (CURA-2602)"
This reverts commit a2a8604c72.
2016-11-28 14:42:18 +01:00
Tim Kuipers 6653f5a557 Merge branch 'master' into feature_recursive_infill_code_review 2016-11-28 14:39:53 +01:00
Tim Kuipers 866e911739 fix: make z_seam_pos settable per mesh (CURA-1461) 2016-11-25 16:43:31 +01:00
Tim Kuipers 83a1a09772 fix: merge fix for PathOrderOptimizer in FffGcodeWriter::processSkin (CURA-1461) 2016-11-21 18:48:10 +01:00
Tim Kuipers 2c425421ec Merge branch 'master' into feature_z_seam_location 2016-11-21 18:45:10 +01:00
Tim Kuipers baf6410a1e fix: PolygonUtils::findClosest failed when only the first polygon was empty (CURA-2602) 2016-11-18 16:33:28 +01:00
Tim Kuipers 4634338c7d fix: use PolygonPointer in ClosestPolygonPoint (CURA-2602)
sometimes functions cannot return a ClosestPolygonPoint, or we start with an empty one untill we find any point
2016-11-18 16:32:56 +01:00
Tim Kuipers a2a8604c72 feat: PolygonPointer as distinct from PolygonRef (CURA-2602)
It was a bad idea to change PolygonRef into PolygonPointer, because then we'd have to check whether it is a nullptr all over the engine...
2016-11-18 16:30:34 +01:00
Tim Kuipers 40a6075022 Merge branch 'master' into feature_recursive_infill_code_review 2016-11-18 13:55:06 +01:00
Tim Kuipers c57ab7d090 fix: getSettingInMicrons should return coord_t, rather than int! (CURA-2602) 2016-11-16 15:25:12 +01:00
Tim Kuipers bd5a82dd86 refactor: split up SubDivCube::generateSubdivisionLines into two functions (CURA-2602)
one public base class function, one private recursive function.
Instead of using a heap-allocated array of pointers to Polygons, just use a stack-allocated array of Polygons
2016-11-16 15:14:16 +01:00
Tim Kuipers 942d409d72 lil refactor: moved constant expression inside function where it's used (CURA-2602) 2016-11-16 14:56:48 +01:00
Tim Kuipers a17d3017b5 lil simplification and reduction of rounding errors (CURA-2602) 2016-11-16 14:35:35 +01:00
Tim Kuipers b79f605ce2 fix: made Polygons::remove be O(1) (CURA-2602)
The order of polygons is never important, except in PolygonsPart where the first polygon is the outer one, but there it should be impossible to remove the first element anyway.
2016-11-16 14:22:45 +01:00
Tim Kuipers ea1c9b80e9 refactor: make private function of SubDivCube::addLineAndCombine (CURA-2602) 2016-11-16 14:21:10 +01:00
Tim Kuipers 1d86f54a8b lil refactor 2016-11-16 13:59:14 +01:00
Tim Kuipers fc909ff3ad fix: subdivcube: max_side_length based on machine dimensions (CURA-2602) 2016-11-16 13:56:41 +01:00
Tim Kuipers 7f54037246 refactor: merged cube properties into one vector of ew struct (CURA-2602) 2016-11-16 13:36:08 +01:00
Tim Kuipers ec7164004e refactor: factored addLine into Polygons class (CURA-2602) 2016-11-16 13:22:28 +01:00
Tim Kuipers a6314a9f80 lil improvements (CURA-2602) 2016-11-16 12:57:09 +01:00
Tim Kuipers 1207291950 rename: src/infill/subDivCube.cpp ==> src/infill/SubDivCube.cpp (CURA-2602) 2016-11-16 12:47:47 +01:00
Tim Kuipers 384071aaeb feat: z seam position (CURA-1461) 2016-11-11 12:02:46 +01:00
mboerwinkle 4b6f672e56 Merge pull request #2 from Ultimaker/feature_recursive_infill_BagelOrb
Some small optimizations and cleanup
2016-11-06 18:23:13 -06:00
Tim Kuipers 6760f38663 fix: properly clean up SubDivCube 2016-10-31 18:51:24 +01:00
Tim Kuipers b1cc8e5abf small optimization: squared distance rather than absolute distance 2016-10-31 18:31:35 +01:00
Tim Kuipers 89d274dfc0 --amend 2016-10-31 18:18:36 +01:00
Tim Kuipers dde776bde7 removed code duplication in SubDivCube constructor 2016-10-31 16:24:12 +01:00
Tim Kuipers edd2c796e1 introduced 3d matrix for 3d rotation 2016-10-31 16:23:19 +01:00
Tim Kuipers 07c64b9361 simplified some math 2016-10-31 16:22:36 +01:00
Tim Kuipers 48b479b675 fix of merge conflict 2016-10-27 23:19:33 +02:00
Tim Kuipers d39a82d13f Merge remote-tracking branch 'mboerwinkle/master' into feature_recursive_infill_BagelOrb 2016-10-27 23:18:39 +02:00
Tim Kuipers 08844ff8d3 Merge branch 'feature_recursive_infill' into feature_recursive_infill_BagelOrb 2016-10-27 23:16:47 +02:00
Tim Kuipers 5f0a844df3 refactor: better naming 2016-10-27 23:14:08 +02:00
Tim Kuipers 2ea58fe795 cleanup: code conventions etc; no more extern SubDivCube 2016-10-27 23:13:12 +02:00
mboerwinkle ed5a0c03c8 Merge pull request #1 from Ultimaker/feature_recursive_infill_BagelOrb
My proposed changes to your PR
2016-10-24 13:48:54 -05:00
Tim Kuipers a954d88d77 feat: cubic subdivision settings 2016-10-21 14:12:57 +02:00
Tim Kuipers d301a8f535 fix: no more memory leak for base_subdiv_cube
destroy it when instantiated and mesh slice data is deleted
2016-10-21 13:56:54 +02:00
Tim Kuipers 8ac14dc725 fix: no more memory leak for base_subdiv_cube
destroy it when instantiated and mesh slice data is deleted
2016-10-21 13:24:50 +02:00
Tim Kuipers 7d4ac28bd2 fix: generate SubDivCube per mesh
There used to be one global SubDivCube which would get reset for each mesh on the build plate.

Also resetting without deleting the object leads to memory loss.
2016-10-21 13:19:17 +02:00
Tim Kuipers f8ea0150fc Merge branch 'master' of https://github.com/mboerwinkle/CuraEngine into feature_recursive_infill 2016-10-21 12:41:33 +02:00
mboerwinkle 06c3729d51 cleaned up a few more things 2016-10-14 14:48:06 +00:00
mboerwinkle 6af4faef1f fixed my editing of CPackConfig.cmake 2016-10-14 04:16:01 +00:00
mboerwinkle 593f3ae353 Merge branch 'master' of git://github.com/Ultimaker/CuraEngine 2016-10-14 04:05:03 +00:00
mboerwinkle 3e0811fdb7 Cleaned up everything best I could! 2016-10-14 03:51:05 +00:00
mboerwinkle 7f31e39ee3 cleaned up the code 2016-10-14 03:37:00 +00:00
mboerwinkle ed5ed001b9 utils/polygon.h looks good. infill.h and .cpp look good. infill/subDivCube.h looks good. Only infill/subDivCube.cpp left to fix. 2016-10-14 02:00:44 +00:00
mboerwinkle f8f14f3968 did some work on bringing my code style up to standards 2016-10-14 01:18:52 +00:00
Tim Kuipers dc41117078 Merge branch 'master' into feature_recursive_infill 2016-10-11 13:30:24 +02:00
mboerwinkle c339dbc8ae added subdivcube infill 2016-10-10 17:13:29 +00:00
98 arquivos alterados com 11482 adições e 1154 exclusões
+17 -3
Ver Arquivo
@@ -54,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
@@ -72,7 +71,6 @@ 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
@@ -85,9 +83,25 @@ set(engine_SRCS # Except main.cpp.
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.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
src/pathPlanning/NozzleTempInsert.cpp
src/pathPlanning/TimeMaterialEstimates.cpp
src/progress/Progress.cpp
src/progress/ProgressStageEstimator.cpp
+1 -1
Ver Arquivo
@@ -44,7 +44,7 @@
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
#define use_lines
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
+5
Ver Arquivo
@@ -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
+1 -1
Ver Arquivo
@@ -2,7 +2,7 @@
#ifndef CONICAL_OVERHANG_H
#define CONICAL_OVERHANG_H
#include "slicer.h"
#include "slicer/Slicer.h"
namespace cura {
+273 -109
Ver Arquivo
@@ -6,6 +6,8 @@
#include "FffProcessor.h"
#include "progress/Progress.h"
#include "wallOverlap.h"
#include "FuzzyWalls.h"
#include "utils/orderOptimizer.h"
namespace cura
{
@@ -18,13 +20,13 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{ // first meshgroup
gcode.resetTotalPrintTimeAndFilament();
gcode.setInitialTemps(*storage.meshgroup);
gcode.setInitialTemps(*storage.meshgroup, getStartExtruder(storage));
}
// set the initial extruder of this meshgroup
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{ // first meshgroup
current_extruder_planned = getSettingAsIndex("adhesion_extruder_nr");
current_extruder_planned = getStartExtruder(storage);
}
else
{
@@ -54,7 +56,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{
processStartingCode(storage);
unsigned int start_extruder_nr = getStartExtruder(storage);
processStartingCode(storage, start_extruder_nr);
}
else
{
@@ -80,6 +83,15 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
}
}
{ // calculate the mesh order for each extruder
int extruder_count = storage.meshgroup->getExtruderCount();
mesh_order_per_extruder.reserve(extruder_count);
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
mesh_order_per_extruder.push_back(calculateMeshOrder(storage, extruder_nr));
}
}
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
{
processLayer(storage, layer_nr, total_layers);
@@ -93,7 +105,7 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
layer_plan_buffer.flush();
constexpr bool force = true;
gcode.writeRetraction(&storage.retraction_config_per_extruder[gcode.getExtruderNr()], force); // retract after finishing each meshgroup
gcode.writeRetraction(storage.retraction_config_per_extruder[gcode.getExtruderNr()], force); // retract after finishing each meshgroup
}
void FffGcodeWriter::setConfigFanSpeedLayerTime(SliceDataStorage& storage)
@@ -193,7 +205,26 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
storage.primeTower.initConfigs(storage.meshgroup);
}
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
unsigned int FffGcodeWriter::getStartExtruder(SliceDataStorage& storage)
{
int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
if (getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::NONE)
{
std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
for (unsigned int extruder_nr = 0; extruder_nr < extruder_is_used.size(); extruder_nr++)
{
start_extruder_nr = extruder_nr;
if (extruder_is_used[extruder_nr])
{
break;
}
}
}
assert(start_extruder_nr >= 0 && start_extruder_nr < storage.meshgroup->getExtruderCount() && "start_extruder_nr must be a valid extruder");
return start_extruder_nr;
}
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage, const unsigned int start_extruder_nr)
{
if (!CommandSocket::isInstantiated())
{
@@ -201,15 +232,13 @@ void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
gcode.writeCode(prefix.c_str());
}
int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE && gcode.getFlavor() != EGCodeFlavor::GRIFFIN)
{
if (getSettingBoolean("material_bed_temp_prepend"))
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature_layer_0") > 0)
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature_layer_0") != 0)
{
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature_layer_0"), getSettingBoolean("material_bed_temp_wait"));
}
@@ -257,8 +286,9 @@ void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
double print_temp_here = (print_temp_0 != 0)? print_temp_0 : train.getSettingInDegreeCelsius("material_print_temperature");
gcode.writeTemperatureCommand(start_extruder_nr, print_temp_here, wait);
gcode.writePrimeTrain(train.getSettingInMillimetersPerSecond("speed_travel"));
extruder_prime_is_planned[start_extruder_nr] = true;
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[start_extruder_nr];
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
}
}
@@ -312,12 +342,11 @@ 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.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
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);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
{
gcode_layer.setExtruder(extruder_nr);
}
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendOptimizedLayerInfo(layer_nr, z, layer_height);
@@ -332,10 +361,14 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
infill_comp.generate(raft_polygons, raftLines);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
if (getExtrudersNeedPrimeDuringFirstLayer())
{
ensureAllExtrudersArePrimed(storage, gcode_layer, layer_nr);
}
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
ensureAllExtrudersArePrimed(storage, gcode_layer, layer_nr);
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_base_fan_speed"));
@@ -346,13 +379,16 @@ 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.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
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
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendOptimizedLayerInfo(layer_nr, z, layer_height);
}
Polygons raftLines;
int offset_from_poly_outline = 0;
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
@@ -362,6 +398,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_interface_fan_speed"));
@@ -374,7 +411,8 @@ 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.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings_per_extruder, combing_mode, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
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())
{
@@ -390,7 +428,8 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
is_inside_mesh_layer_part = gcode_layer.getIsInsideMesh();
gcode_layer.processFanSpeedAndMinimalLayerTime();
gcode_layer.overrideFanSpeeds(train->getSettingInPercentage("raft_surface_fan_speed"));
}
@@ -432,7 +471,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
}
bool avoid_other_parts = false;
int avoid_distance = 0; // minimal avoid distance is zero
coord_t avoid_distance = 0; // minimal avoid distance is zero
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
{
if (gcode.getExtruderIsUsed(extr_nr))
@@ -447,7 +486,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
}
}
int max_inner_wall_width = 0;
coord_t max_inner_wall_width = 0;
for (SettingsBaseVirtual& mesh_settings : storage.meshes)
{
max_inner_wall_width = std::max(max_inner_wall_width, mesh_settings.getSettingInMicrons((mesh_settings.getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0"));
@@ -456,22 +495,26 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, 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.
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
int extruder_nr = gcode_layer.getExtruder();
if (storage.skirt_brim[extruder_nr].size() > 0)
{
gcode_layer.setExtruder(extruder_nr);
processSkirtBrim(storage, gcode_layer, extruder_nr);
}
}
if (include_helper_parts)
{ // handle shield(s) first in a layer so that chances are higher that the other nozzle is wiped (for the ooze shield)
processOozeShield(storage, gcode_layer, std::max(0, layer_nr));
processDraftShield(storage, gcode_layer, std::max(0, layer_nr));
}
int support_skin_extruder_nr = getSettingAsIndex("support_interface_extruder_nr");
int support_infill_extruder_nr = (layer_nr <= 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
//Figure out in which order to print the meshes, do this by looking at the current extruder and preferer the meshes that use that extruder.
std::vector<int> extruder_order = calculateExtruderOrder(storage, gcode_layer.getExtruder());
for (int extruder_nr : extruder_order)
{
@@ -483,9 +526,36 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
if (layer_nr >= 0)
{
std::vector<unsigned int> mesh_order = calculateMeshOrder(storage, extruder_nr);
for (unsigned int mesh_idx : mesh_order)
std::vector<unsigned int>& mesh_order = mesh_order_per_extruder[extruder_nr];
unsigned int mesh_order_idx_starting_mesh = 0;
{ // calculate mesh_order_idx_starting_mesh
Point layer_start_position = last_position_planned;
if (storage.getSettingBoolean("start_layers_at_same_position"))
{
layer_start_position = Point(storage.getSettingInMicrons("layer_start_x"), storage.getSettingInMicrons("layer_start_y"));
}
coord_t best_dist2 = std::numeric_limits<coord_t>::max();
for (unsigned int mesh_order_idx = 0; mesh_order_idx < mesh_order.size(); mesh_order_idx++)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
{
Point middle = (part.boundaryBox.min + part.boundaryBox.max) / 2;
coord_t dist2 = vSize2(middle - layer_start_position);
if (dist2 < best_dist2)
{
best_dist2 = dist2;
mesh_order_idx_starting_mesh = mesh_order_idx;
}
}
}
}
for (unsigned int mesh_iterator_idx = 0; mesh_iterator_idx < mesh_order.size(); mesh_iterator_idx++)
{
unsigned int mesh_order_idx = (mesh_iterator_idx + mesh_order_idx_starting_mesh) % mesh_order.size();
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
@@ -499,7 +569,7 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, int layer_nr, unsig
}
}
if (include_helper_parts && layer_nr == 0)
if (layer_nr == 0 && getExtrudersNeedPrimeDuringFirstLayer())
{
ensureAllExtrudersArePrimed(storage, gcode_layer, layer_nr);
}
@@ -513,17 +583,31 @@ 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();
gcode_layer.processFanSpeedAndMinimalLayerTime();
}
bool FffGcodeWriter::getExtrudersNeedPrimeDuringFirstLayer()
{
switch(gcode.getFlavor())
{
case EGCodeFlavor::GRIFFIN:
return true;
default:
return false; // TODO: change this once priming for other firmware types is implemented
}
}
void FffGcodeWriter::ensureAllExtrudersArePrimed(SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr)
{
//Add skirt for all extruders which haven't primed the skirt or brim yet.
// Add prime for all extruders which haven't primed yet.
std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
if (gcode.getExtruderIsUsed(extruder_nr) && !skirt_brim_is_processed[extruder_nr])
{
if (extruder_is_used[extruder_nr] && !extruder_prime_is_planned[extruder_nr])
{ // prime before the current gcode layer plan is written to gcode
setExtruder_addPrime(storage, gcode_layer, layer_nr, extruder_nr);
}
}
@@ -606,15 +690,26 @@ std::vector<int> FffGcodeWriter::calculateExtruderOrder(SliceDataStorage& storag
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int extruder_nr)
{
std::vector<unsigned int> ret;
OrderOptimizer<unsigned int> mesh_idx_order_optimizer;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingAsIndex("extruder_nr") == extruder_nr)
{
ret.push_back(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);
}
}
std::list<unsigned int> mesh_indices_order = mesh_idx_order_optimizer.optimize();
std::vector<unsigned int> ret;
ret.reserve(mesh_indices_order.size());
for (unsigned int mesh_order_idx : mesh_indices_order)
{
const unsigned int mesh_idx = mesh_idx_order_optimizer.items[mesh_order_idx].second;
ret.push_back(mesh_idx);
}
return ret;
}
@@ -643,8 +738,24 @@ 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");
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, mesh->getSettingInMicrons("wall_0_wipe_dist"), mesh->getSettingBoolean("magic_spiralize"));
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
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);
}
@@ -709,73 +820,23 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
Point layer_start_position = last_position_planned;
if (storage.getSettingBoolean("start_layers_at_same_position"))
{
layer_start_position = Point(storage.getSettingInMicrons("layer_start_x"), storage.getSettingInMicrons("layer_start_y"));
}
PathOrderOptimizer part_order_optimizer(layer_start_position, z_seam_type);
PathOrderOptimizer part_order_optimizer(layer_start_position, z_seam_pos, z_seam_type);
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
part_order_optimizer.addPolygon(layer->parts[partNr].insets[0][0]);
}
part_order_optimizer.optimize();
bool skin_alternate_rotation = mesh->getSettingBoolean("skin_alternate_rotation") && ( mesh->getSettingAsCount("top_layers") >= 4 || mesh->getSettingAsCount("bottom_layers") >= 4 );
for(int order_idx : part_order_optimizer.polyOrder)
for (int part_idx : part_order_optimizer.polyOrder)
{
SliceLayerPart& part = layer->parts[order_idx];
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
int infill_angle = 45;
if ((infill_pattern == EFillMethod::LINES || infill_pattern == EFillMethod::ZIG_ZAG))
{
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh->getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), 1)));
if ((layer_nr / combined_infill_layers) & 1)
{ // switch every [combined_infill_layers] layers
infill_angle += 90;
}
}
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
int infill_overlap = mesh->getSettingInMicrons("infill_overlap_mm");
gcode_layer.setIsInside(&part); // going to print inside stuff below
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
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)
{
skin_angle += 90; // should coincide with infill_angle (if both skin and infill are lines) so that the first top layer is orthogonal to the last infill layer
}
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
skin_angle -= 45;
int64_t skin_overlap = mesh->getSettingInMicrons("skin_overlap_mm");
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle);
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
{
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1, part);
}
gcode_layer.setIsInside(nullptr);
SliceLayerPart& part = layer->parts[part_idx];
addMeshPartToGCode(storage, mesh, part, gcode_layer, layer_nr);
}
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -783,6 +844,62 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
}
}
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 );
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
int infill_angle = 45;
if ((infill_pattern == EFillMethod::LINES || infill_pattern == EFillMethod::ZIG_ZAG))
{
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh->getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1)));
if ((layer_nr / combined_infill_layers) & 1)
{ // switch every [combined_infill_layers] layers
infill_angle += 90;
}
}
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
int infill_overlap = mesh->getSettingInMicrons("infill_overlap_mm");
gcode_layer.setIsInside(true); // going to print inside stuff below
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
Point z_seam_pos(mesh->getSettingInMicrons("z_seam_x"), mesh->getSettingInMicrons("z_seam_y"));
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type, z_seam_pos);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle);
}
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)
{
skin_angle += 90; // should coincide with infill_angle (if both skin and infill are lines) so that the first top layer is orthogonal to the last infill layer
}
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
skin_angle -= 45;
int64_t skin_overlap = mesh->getSettingInMicrons("skin_overlap_mm");
processSkinAndPerimeterGaps(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle);
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
{
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
gcode_layer.setIsInside(false);
}
@@ -810,7 +927,7 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
}
Infill infill_comp(infill_pattern, part.infill_area_per_combine_per_density[density_idx][combine_idx], 0, infill_line_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift);
infill_comp.generate(infill_polygons, infill_lines);
infill_comp.generate(infill_polygons, infill_lines, mesh);
}
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[combine_idx]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[combine_idx], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
@@ -866,7 +983,7 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
infill_line_distance_here /= 2;
}
Infill infill_comp(pattern, part.infill_area_per_combine_per_density[density_idx][0], 0, infill_line_width, infill_line_distance_here, infill_overlap, infill_angle, z, infill_shift);
infill_comp.generate(infill_polygons, infill_lines);
infill_comp.generate(infill_polygons, infill_lines, mesh);
}
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
@@ -879,7 +996,7 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
}
}
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
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");
@@ -894,7 +1011,9 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
}
if (static_cast<int>(layer_nr) == mesh->getSettingAsCount("bottom_layers") && part.insets.size() > 0)
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, nullptr, EZSeamType::SHORTEST, mesh->getSettingInMicrons("wall_0_wipe_dist"), false);
WallOverlapComputation* wall_overlap_computation(nullptr);
int wall_0_wipe_dist(0);
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, wall_overlap_computation, EZSeamType::SHORTEST, z_seam_pos, mesh->getSettingInMicrons("wall_0_wipe_dist"), wall_0_wipe_dist);
}
}
int processed_inset_number = -1;
@@ -907,15 +1026,23 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
}
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"))
{
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type, 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, mesh->getSettingInMicrons("wall_0_wipe_dist"), spiralize);
delete flow_adjuster;
}
}
else
@@ -936,7 +1063,7 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
}
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, 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();
@@ -944,7 +1071,8 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
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;
PathOrderOptimizer part_order_optimizer(gcode_layer.getLastPosition(), EZSeamType::SHORTEST);
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++)
{
PolygonsPart& outline = part.skin_parts[skin_part_idx].outline;
@@ -988,13 +1116,13 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
{
// add perimeter gaps between the outer skin inset and the innermost wall
const Polygons outer = skin_part.outline;
const Polygons inner = skin_part.insets[0].offset(mesh->insetX_config.getLineWidth() / 2 + perimeter_gaps_extra_offset * 2);
const Polygons inner = skin_part.insets[0].offset(mesh->insetX_config.getLineWidth() / 2 + perimeter_gaps_extra_offset);
perimeter_gaps.add(outer.difference(inner));
for (unsigned int inset_idx = 1; inset_idx < skin_part.insets.size(); inset_idx++)
{ // add perimeter gaps between consecutive skin walls
const Polygons outer = skin_part.insets[inset_idx - 1].offset(-1 * mesh->insetX_config.getLineWidth() / 2 - perimeter_gaps_extra_offset);
const Polygons inner = skin_part.insets[inset_idx].offset(mesh->insetX_config.getLineWidth() / 2 + perimeter_gaps_extra_offset);
const Polygons inner = skin_part.insets[inset_idx].offset(mesh->insetX_config.getLineWidth() / 2);
perimeter_gaps.add(outer.difference(inner));
}
}
@@ -1034,13 +1162,31 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
{ // handle perimeter gaps of normal insets
Polygons perimeter_gaps;
int line_width = mesh->inset0_config.getLineWidth();
for (unsigned int inset_idx = 1; inset_idx < part.insets.size(); inset_idx++)
for (unsigned int inset_idx = 0; inset_idx < part.insets.size() - 1; inset_idx++)
{
const Polygons outer = part.insets[inset_idx - 1].offset(-1 * line_width / 2 - perimeter_gaps_extra_offset);
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();
const Polygons inner = part.insets[inset_idx].offset(line_width / 2 + perimeter_gaps_extra_offset);
Polygons inner = part.insets[inset_idx + 1].offset(line_width / 2);
perimeter_gaps.add(outer.difference(inner));
}
{ // gap between inner wall and skin/infill
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 (SkinPart& skin_part : part.skin_parts)
{
inner.add(skin_part.outline);
}
perimeter_gaps.add(outer.difference(inner));
}
}
Polygons skin_polygons; // unused
Polygons skin_lines; // soon to be generated gap filler lines
@@ -1227,11 +1373,29 @@ void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanne
if (extruder_changed)
{
if (!extruder_prime_is_planned[extruder_nr])
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
// move to prime position
bool prime_pos_is_abs = train->getSettingBoolean("extruder_prime_pos_abs");
Point prime_pos = Point(train->getSettingInMicrons("extruder_prime_pos_x"), train->getSettingInMicrons("extruder_prime_pos_y"));
gcode_layer.addTravel(prime_pos_is_abs? prime_pos : gcode_layer.getLastPosition() + prime_pos);
gcode_layer.planPrime();
extruder_prime_is_planned[extruder_nr] = true;
}
assert(extruder_prime_is_planned[extruder_nr] && "extruders should be primed before they are used!");
if (layer_nr == 0 && !skirt_brim_is_processed[extruder_nr])
{
processSkirtBrim(storage, gcode_layer, extruder_nr);
}
addPrimeTower(storage, gcode_layer, layer_nr, previous_extruder);
if (layer_nr >= -Raft::getFillerLayerCount(storage))
{
addPrimeTower(storage, gcode_layer, layer_nr, previous_extruder);
}
}
}
+57 -12
Ver Arquivo
@@ -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)
@@ -65,18 +65,28 @@ private:
*/
bool skirt_brim_is_processed[MAX_EXTRUDERS];
std::vector<std::vector<unsigned int>> mesh_order_per_extruder; //!< For each extruder, the cyclic order of the meshes (the first element is not the starting element per se)
/*!
* For each extruder whether priming has already been planned
*/
bool extruder_prime_is_planned[MAX_EXTRUDERS];
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder; //!< The settings used relating to minimal layer time and fan speeds. Configured for each extruder.
Point last_position_planned; //!< The position of the head before planning the next layer
int current_extruder_planned; //!< The extruder train in use before planning the next layer
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, max_object_height(0)
, layer_plan_buffer(this, gcode)
, extruder_prime_is_planned {} // initialize all values in array with [false]
, last_position_planned(no_point)
, current_extruder_planned(0) // changed somewhere early in FffGcodeWriter::writeGCode
, is_inside_mesh_layer_part(false)
{
max_object_height = 0;
}
/*!
@@ -173,15 +183,26 @@ private:
* \param[out] storage The data storage to which to save the configurations.
*/
void initConfigs(SliceDataStorage& storage);
/*!
* Get the extruder with which to start the print.
*
* Generally this is the adhesion_extruder_nr, but in case the platform adhesion type is none,
* the extruder with lowest number which is used on the first layer is used as initial extruder.
*
* \param[in] storage where to get settings from.
*/
unsigned int getStartExtruder(SliceDataStorage& storage);
/*!
* Set temperatures and perform initial priming.
*
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
*
* \param[in] storage where the slice data is stored.
* \param[in] start_extruder_nr The extruder with which to start the print.
*/
void processStartingCode(SliceDataStorage& storage);
void processStartingCode(SliceDataStorage& storage, const unsigned int start_extruder_nr);
/*!
* Move up and over the already printed meshgroups to print the next meshgroup.
@@ -210,6 +231,13 @@ private:
*/
void processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers);
/*!
* Whether the extruders need to be primed separately just before they are used.
*
* \return whether the extruders need to be primed separately just before they are used
*/
bool getExtrudersNeedPrimeDuringFirstLayer();
/*!
* Plan priming of all used extruders which haven't been primed yet
* \param[in] storage where the slice data is stored.
@@ -287,15 +315,27 @@ private:
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 gcodeLayer.
* Add a single layer from a single mesh-volume to the layer plan \p gcode_layer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param gcode_layer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, 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.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param part The part to add
* \param gcode_layer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
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.
@@ -329,20 +369,25 @@ private:
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \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, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type);
void processInsets(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos);
/*!
* Add the gcode of the top/bottom skin of the given part.
* Add the gcode of the top/bottom skin of the given part and of the perimeter gaps.
*
* Perimter gaps are generated for skin outlines and printed while the skin fill of the skin part is printed.
* Perimeter gaps between the walls are added to the gcode afterwards.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param skin_overlap The distance by which the skin overlaps with the wall insets.
* \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 processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, 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.
+41 -84
Ver Arquivo
@@ -2,16 +2,19 @@
#include <algorithm>
#include <map> // multimap (ordered map allowing duplicate keys)
#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"
@@ -53,7 +56,7 @@ unsigned int FffPolygonGenerator::getDraftShieldLayerCount(const unsigned int to
case DraftShieldHeightLimitation::FULL:
return total_layers;
case DraftShieldHeightLimitation::LIMITED:
return std::max(0, (getSettingInMicrons("draft_shield_height") - getSettingInMicrons("layer_height_0")) / getSettingInMicrons("layer_height") + 1);
return std::max((coord_t)0, (getSettingInMicrons("draft_shield_height") - getSettingInMicrons("layer_height_0")) / getSettingInMicrons("layer_height") + 1);
}
}
@@ -85,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)
@@ -107,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);
@@ -116,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"));
@@ -125,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"))
{
@@ -134,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"))
{
@@ -297,6 +315,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
// handle helpers
storage.primeTower.generatePaths(storage);
storage.primeTower.subtractFromSupport(storage);
logDebug("Processing ooze shield\n");
processOozeShield(storage);
@@ -356,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;
@@ -460,15 +479,15 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
// combine infill
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), 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"))
//SubDivCube Pre-compute Octree
if (mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV)
{
processFuzzyWalls(mesh);
SubDivCube::precomputeOctree(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);
}
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
@@ -699,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
+3 -3
Ver Arquivo
@@ -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;
}
+218
Ver Arquivo
@@ -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
+52
Ver Arquivo
@@ -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
+2 -4
Ver Arquivo
@@ -251,7 +251,7 @@ void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& ex
time_window += prev_extruder_plan_time;
heated_pre_travel_time = prev_extruder_plan.heated_pre_travel_time;
if (prev_extruder_plan.estimates.getTotalUnretractedTime() > 0 && prev_extruder_plan.estimates.getMaterial() > 0)
if (prev_extruder_plan.estimates.getTotalUnretractedTime() > 0)
{ // handle temp statistics
assert(prev_extruder_plan.printing_temperature != -1 && "Previous extruder plan should already have a temperature planned");
weighted_average_print_temp += prev_extruder_plan.printing_temperature * prev_extruder_plan_time;
@@ -349,9 +349,7 @@ void LayerPlanBuffer::insertTempCommands()
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double time = extruder_plan.estimates.getTotalUnretractedTime();
if (time <= 0.0
|| extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
)
if (time <= 0.0)
{
continue;
}
+171 -22
Ver Arquivo
@@ -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
@@ -58,6 +66,7 @@ int MeshGroup::getExtruderCount() const
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
assert((int)extruder_nr >= 0 && (int)extruder_nr < getSettingAsCount("machine_extruder_count") && "only valid extruder trains may be requested!");
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
@@ -89,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);
@@ -106,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);
@@ -119,9 +128,9 @@ Point3 MeshGroup::max() const
void MeshGroup::clear()
{
for(Mesh& m : meshes)
for (Mesh* m : meshes)
{
m.clear();
m->clear();
}
}
@@ -139,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
@@ -155,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);
}
}
@@ -174,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);
}
}
@@ -328,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;
@@ -335,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
Ver Arquivo
@@ -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
+41
Ver Arquivo
@@ -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
+13 -24
Ver Arquivo
@@ -82,12 +82,6 @@ Preheat::WarmUpResult Preheat::getWarmUpPointAfterCoolDown(double time_window, u
result.heating_time = extra_heatup_time;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_start;
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = temp_start;
return result;
}
}
else
{
@@ -95,13 +89,14 @@ Preheat::WarmUpResult Preheat::getWarmUpPointAfterCoolDown(double time_window, u
result.heating_time = 0;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_end;
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = temp_end;
return result;
}
}
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = std::min(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
@@ -150,12 +145,6 @@ Preheat::CoolDownResult Preheat::getCoolDownPointAfterWarmUp(double time_window,
result.cooling_time = 0;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_end;
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = temp_end;
return result;
}
}
else
{
@@ -163,12 +152,12 @@ Preheat::CoolDownResult Preheat::getCoolDownPointAfterWarmUp(double time_window,
result.cooling_time = extra_cooldown_time;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_start;
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = temp_start;
return result;
}
}
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = std::max(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double cool_down_time = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
-1
Ver Arquivo
@@ -117,7 +117,6 @@ public:
{
return config_per_extruder[extruder_nr].flow_dependent_temperature;
}
public:
/*!
* Get the optimal temperature corresponding to a given average flow,
* or the initial layer temperature.
+23 -12
Ver Arquivo
@@ -12,7 +12,7 @@
namespace cura
{
PrimeTower::PrimeTower()
PrimeTower::PrimeTower(const SliceDataStorage& storage)
: is_hollow(false)
, wipe_from_middle(false)
, current_pre_wipe_location_idx(0)
@@ -21,6 +21,13 @@ PrimeTower::PrimeTower()
{
last_prime_tower_poly_printed[extruder_nr] = -1;
}
enabled = storage.getSettingBoolean("prime_tower_enable")
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
&& storage.getSettingInMicrons("prime_tower_size") > 10;
if (enabled)
{
generateGroundpoly(storage);
}
}
@@ -83,10 +90,7 @@ void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
enabled = storage.max_print_height_second_to_last_extruder >= 0
&& storage.getSettingBoolean("prime_tower_enable")
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
&& storage.getSettingInMicrons("prime_tower_size") > 10;
enabled &= storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches.
if (enabled)
{
generatePaths_denseInfill(storage);
@@ -100,8 +104,6 @@ void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
int extra_infill_shift = 0;
generateGroundpoly(storage);
int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position
for (int extruder = 0; extruder < extruder_count; extruder++)
@@ -160,7 +162,7 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode
preWipe(storage, gcodeLayer, new_extruder);
}
addToGcode_denseInfill(storage, gcodeLayer, gcode, layer_nr, prev_extruder, new_extruder);
addToGcode_denseInfill(gcodeLayer, layer_nr, new_extruder);
// post-wipe:
if (post_wipe)
@@ -169,16 +171,16 @@ void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode
}
}
void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder)
void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcodeLayer, const int layer_nr, const int extruder)
{
ExtrusionMoves& pattern = patterns_per_extruder[new_extruder][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
ExtrusionMoves& pattern = patterns_per_extruder[extruder][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
GCodePathConfig& config = config_per_extruder[new_extruder];
GCodePathConfig& config = config_per_extruder[extruder];
gcodeLayer.addPolygonsByOptimizer(pattern.polygons, &config);
gcodeLayer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
last_prime_tower_poly_printed[extruder] = layer_nr;
}
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
@@ -295,5 +297,14 @@ void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_la
gcode_layer.addExtrusionMove(end, &config_per_extruder[extruder_nr], SpaceFillType::None, flow);
}
void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
{
const Polygons outside_polygon = ground_poly.getOutsidePolygons();
for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++)
{
storage.support.supportLayers[layer].supportAreas = storage.support.supportLayers[layer].supportAreas.difference(outside_polygon);
}
}
}//namespace cura
+26 -10
Ver Arquivo
@@ -1,3 +1,6 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
@@ -30,8 +33,6 @@ private:
Polygons polygons;
Polygons lines;
};
bool enabled; //!< Whether the prime tower is enabled
int extruder_count; //!< number of extruders
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
@@ -49,10 +50,19 @@ private:
int current_pre_wipe_location_idx; //!< Index into \ref PrimeTower::wipe_locations of where to pre-wipe the nozzle
public:
bool enabled; //!< Whether the prime tower is enabled.
Polygons ground_poly; //!< The outline of the prime tower to be used for each layer
std::vector<std::vector<ExtrusionMoves>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
/*!
* \brief Creates a prime tower instance that will determine where and how
* the prime tower gets printed.
*
* \param storage A storage where it retrieves the prime tower settings.
*/
PrimeTower(const SliceDataStorage& storage);
/*!
* Initialize \ref PrimeTower::config_per_extruder with speed and line width settings.
*
@@ -85,8 +95,6 @@ public:
*/
void generatePaths(const SliceDataStorage& storage);
PrimeTower(); //!< basic constructor
/*!
* Add path plans for the prime tower to the \p gcode_layer
*
@@ -97,6 +105,15 @@ public:
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
*/
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder);
/*!
* \brief Subtract the prime tower from the support areas in storage.
*
* \param storage The storage where to find the support from which to
* subtract a prime tower.
*/
void subtractFromSupport(SliceDataStorage& storage);
private:
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
@@ -134,16 +151,15 @@ private:
/*!
* \see PrimeTower::addToGcode
*
*
* Add path plans for the prime tower to the \p gcode_layer
*
* \param storage where to get settings from; where to get the maximum height of the prime tower from
*
* \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths
* \param layer_nr The layer for which to generate the prime tower paths
* \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
* \param extruder The extruder we just switched to, with which the prime
* tower paths should be drawn.
*/
void addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder);
void addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder);
/*!
* Plan the moves for wiping the current nozzles oozed material before starting to print the prime tower.
+9 -2
Ver Arquivo
@@ -1,4 +1,7 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
//Copyright (C) 2013 David Braam
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkirtBrim.h"
#include "support.h"
@@ -19,6 +22,7 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
{ // add brim underneath support by removing support where there's brim around the model
const bool include_helper_parts = false; // include manually below
first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
Polygons first_layer_empty_holes;
if (outside_only)
{
@@ -44,7 +48,10 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
first_layer_outline.add(support_layer.supportAreas);
first_layer_outline.add(support_layer.skin);
}
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
if (storage.primeTower.enabled)
{
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
}
}
constexpr int join_distance = 20;
first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
+2 -2
Ver Arquivo
@@ -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);
}
+1 -1
Ver Arquivo
@@ -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"
+11 -10
Ver Arquivo
@@ -18,8 +18,9 @@ void Wireframe2gcode::writeGCode()
{
gcode.preSetup(wireFrame.meshgroup);
gcode.setInitialTemps(*wireFrame.meshgroup);
const unsigned int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr"); // TODO: figure out how Wireframe works with dual extrusion
gcode.setInitialTemps(*wireFrame.meshgroup, start_extruder_nr);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
@@ -159,7 +160,7 @@ void Wireframe2gcode::writeGCode()
gcode.setZ(maxObjectHeight);
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.updateTotalPrintTime();
@@ -246,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;
@@ -259,7 +260,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
Point3 lower = to - lowering;
gcode.writeMove(lower, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
gcode.writeMove(to + lowering, speedUp, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -268,7 +269,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
} else
{
gcode.writeMove(to, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -467,14 +468,14 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
void Wireframe2gcode::writeMoveWithRetract(Point3 to)
{
if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
void Wireframe2gcode::writeMoveWithRetract(Point to)
{
if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
@@ -562,7 +563,7 @@ void Wireframe2gcode::processStartingCode()
{
if (getSettingBoolean("material_bed_temp_prepend"))
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") != 0)
{
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
}
@@ -603,7 +604,7 @@ void Wireframe2gcode::processStartingCode()
constexpr bool wait = true;
gcode.writeTemperatureCommand(start_extruder_nr, getSettingInDegreeCelsius("material_print_temperature"), wait);
gcode.writePrimeTrain(getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
}
}
+17 -17
Ver Arquivo
@@ -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.
+4 -4
Ver Arquivo
@@ -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)
{
+31 -27
Ver Arquivo
@@ -11,6 +11,8 @@
namespace cura {
double layer_height; //!< report basic layer height in RepRap gcode file.
GCodeExport::GCodeExport()
: output_stream(&std::cout)
, currentPosition(0,0,MM2INT(20))
@@ -49,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;
}
}
@@ -67,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;
@@ -97,6 +99,8 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
machine_name = meshgroup->getSettingString("machine_name");
layer_height = meshgroup->getSettingInMillimeters("layer_height");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
@@ -109,20 +113,15 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
estimateCalculator.setFirmwareDefaults(meshgroup);
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
void GCodeExport::setInitialTemps(const MeshGroup& settings, const unsigned int start_extruder_nr)
{
int start_extruder_nr = 0;
if (settings.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
start_extruder_nr = settings.getSettingAsIndex("adhesion_extruder_nr");
}
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
const ExtruderTrain& train = *settings.getExtruderTrain(extr_nr);
double print_temp_0 = train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
double print_temp_here = (print_temp_0 != 0)? print_temp_0 : train.getSettingInDegreeCelsius("material_print_temperature");
double temp = ((int)extr_nr == start_extruder_nr)? print_temp_here : train.getSettingInDegreeCelsius("material_standby_temperature");
double temp = (extr_nr == start_extruder_nr)? print_temp_here : train.getSettingInDegreeCelsius("material_standby_temperature");
setInitialTemp(extr_nr, temp);
}
@@ -196,6 +195,11 @@ std::string GCodeExport::getFileHeader(const double* print_time, const std::vect
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// TODO: the second nozzle size isn't always initiated! ";NOZZLE_DIAMETER2:"
}
else if (flavor == EGCodeFlavor::REPRAP)
{
prefix << ";Filament used: " << ((filament_used.size() >= 1)? filament_used[0] / (1000 * extruder_attr[0].filament_area) : 0) << "m" << new_line;
prefix << ";Layer height: " << layer_height << new_line;
}
return prefix.str();
}
}
@@ -639,7 +643,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool extruder_switch)
void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bool extruder_switch)
{
ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
@@ -656,7 +660,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
}
double old_retraction_e_amount = extr_attr.retraction_e_amount_current;
double new_retraction_e_amount = mmToE(config->distance);
double new_retraction_e_amount = mmToE(config.distance);
double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount;
if (std::abs(retraction_diff_e_amount) < 0.000001)
{
@@ -666,23 +670,23 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
while (int(extruded_volume_at_previous_n_retractions.size()) > config.retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
{
// extruder switch could have introduced data which falls outside the retraction window
// also the retraction_count_max could have changed between the last retraction and this
extruded_volume_at_previous_n_retractions.pop_back();
}
if (!force && config->retraction_count_max <= 0)
if (!force && config.retraction_count_max <= 0)
{
return;
}
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extr_attr.filament_area)
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config.retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config.retraction_extrusion_window * extr_attr.filament_area)
{
return;
}
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
if (int(extruded_volume_at_previous_n_retractions.size()) == config.retraction_count_max + 1)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
@@ -705,17 +709,17 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
}
else
{
double speed = ((retraction_diff_e_amount < 0.0)? config->speed : extr_attr.last_retraction_prime_speed) * 60;
double speed = ((retraction_diff_e_amount < 0.0)? config.speed : extr_attr.last_retraction_prime_speed) * 60;
current_e_value += retraction_diff_e_amount;
*output_stream << "G1 F" << PrecisionedDouble{1, speed} << " "
<< extr_attr.extruderCharacter << PrecisionedDouble{5, current_e_value} << new_line;
currentSpeed = speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
extr_attr.last_retraction_prime_speed = config->primeSpeed;
extr_attr.last_retraction_prime_speed = config.primeSpeed;
}
extr_attr.retraction_e_amount_current = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
extr_attr.prime_volume += config->prime_volume;
extr_attr.prime_volume += config.prime_volume;
}
@@ -772,7 +776,7 @@ void GCodeExport::switchExtruder(int new_extruder, const RetractionConfig& retra
bool force = true;
bool extruder_switch = true;
writeRetraction(&const_cast<RetractionConfig&>(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
@@ -904,7 +908,7 @@ void GCodeExport::writeMaxZFeedrate(double max_z_feedrate)
{
if (current_max_z_feedrate != max_z_feedrate)
{
*output_stream << "M203 Z" << int(max_z_feedrate * 60) << new_line;
*output_stream << "M203 Z" << PrecisionedDouble{2, max_z_feedrate} << new_line;
current_max_z_feedrate = max_z_feedrate;
estimateCalculator.setMaxZFeedrate(max_z_feedrate);
}
+6 -2
Ver Arquivo
@@ -261,7 +261,7 @@ private:
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
public:
void writeRetraction(RetractionConfig* config, bool force = false, bool extruder_switch = false);
void writeRetraction(const RetractionConfig& config, bool force = false, bool extruder_switch = false);
/*!
* Start a z hop with the given \p hop_height
@@ -349,13 +349,17 @@ public:
* See FffGcodeWriter::processStartingCode
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
* \param start_extruder_nr The extruder with which to start this print
*/
void setInitialTemps(const MeshGroup& settings);
void setInitialTemps(const MeshGroup& settings, const unsigned int start_extruder_nr);
/*!
* Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
* This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
*
* \warning This function must be called before any of the layers in the meshgroup are written to file!
* That's because it sets the current temperature in the gcode!
*
* \param extruder_nr The extruder number for which to better specify the temp
* \param temp The temp at which the nozzle should be at startup
*/
+47 -50
Ver Arquivo
@@ -9,19 +9,6 @@
namespace cura {
TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other)
{
extrude_time -= other.extrude_time;
unretracted_travel_time -= other.unretracted_travel_time;
retracted_travel_time -= other.retracted_travel_time;
material -= other.material;
return *this;
}
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)
: extruder(extruder)
@@ -77,6 +64,7 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceF
paths.emplace_back();
GCodePath* ret = &paths.back();
ret->retract = false;
ret->perform_prime = false;
ret->perform_z_hop = false;
ret->config = config;
ret->done = false;
@@ -93,7 +81,7 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, 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)
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)
: storage(storage)
, layer_nr(layer_nr)
, is_initial_layer(layer_nr == 0 - Raft::getTotalExtraLayers(storage))
@@ -109,8 +97,8 @@ GCodePlanner::GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int l
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]);
comb = nullptr;
was_inside = storage.getPartInside(layer_nr, start_position);
is_inside = nullptr; // assumes the next move will not be to inside a layer part (overwritten just before going into a layer part)
was_inside = is_inside_mesh;
is_inside = false; // assumes the next move will not be to inside a layer part (overwritten just before going into a layer part)
if (combing_mode != CombingMode::OFF)
{
comb = new Comb(storage, layer_nr, comb_boundary_inside, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
@@ -139,7 +127,14 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
}
if (layer_nr < 0)
{ // when a raft is present
return Polygons();
if (combing_mode == CombingMode::NO_SKIN)
{
return Polygons();
}
else
{
return storage.raftOutline.offset(MM2INT(0.1));
}
}
else
{
@@ -167,7 +162,7 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
}
}
void GCodePlanner::setIsInside(SliceLayerPart* _is_inside)
void GCodePlanner::setIsInside(bool _is_inside)
{
is_inside = _is_inside;
}
@@ -178,7 +173,7 @@ bool GCodePlanner::setExtruder(int extruder)
{
return false;
}
setIsInside(nullptr);
setIsInside(false);
{ // handle end position of the prev extruder
SettingsBaseVirtual* train = getLastPlannedExtruderTrainSettings();
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
@@ -225,27 +220,11 @@ bool GCodePlanner::setExtruder(int extruder)
return true;
}
void GCodePlanner::moveInsideCombBoundary(int distance, const SliceLayerPart& part)
void GCodePlanner::moveInsideCombBoundary(int distance)
{
// this function is to be used to move from the boudary of a part to inside the part
int max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
// this function is to be used to move from the boudary of a part to inside the part
Point p = lastPosition; // copy, since we are going to move p
{ // first move inside the last part, so that the chance is higher that we move inside the same part
const Polygons* comb_boundary_here;
if (part.insets.size() > 1)
{
comb_boundary_here = &part.insets[1];
}
else if (part.insets.size() == 1)
{
comb_boundary_here = &part.insets[0];
}
else
{
comb_boundary_here = &part.print_outline;
}
PolygonUtils::moveInside(*comb_boundary_here, p, distance);
}
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
{
//Move inside again, so we move out of tight 90deg corners
@@ -337,7 +316,7 @@ GCodePath& GCodePlanner::addTravel(Point p)
if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc)
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1, *was_inside);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
path = getLatestPathWithConfig(&travel_config, SpaceFillType::None);
path->retract = true;
@@ -361,6 +340,14 @@ GCodePath& GCodePlanner::addTravel_simple(Point p, GCodePath* path)
return *path;
}
void GCodePlanner::planPrime()
{
forceNewPathStart();
GCodePath& prime_travel = addTravel_simple(lastPosition + Point(0, 100));
prime_travel.retract = false;
prime_travel.perform_prime = true;
forceNewPathStart();
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
@@ -368,21 +355,25 @@ void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillT
lastPosition = p;
}
void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, 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)
{
Point& p1 = polygon[start_idx];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
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)
@@ -416,13 +407,13 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, GCodePathConfig
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, 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)
{
return;
}
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_pos, z_seam_type);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
orderOptimizer.addPolygon(polygons[poly_idx]);
@@ -430,7 +421,7 @@ void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* c
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(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
@@ -696,7 +687,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
// prime extruder if it hadn't been used yet
gcode.writePrimeTrain(storage.meshgroup->getExtruderTrain(extruder)->getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
if (extruder_plan.prev_extruder_standby_temp)
{ // turn off previous extruder
@@ -712,7 +703,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
}
else if (extruder_plan_idx == 0 && layer_nr != 0 && storage.meshgroup->getExtruderTrain(extruder)->getSettingBoolean("retract_at_layer_change"))
{
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
}
gcode.writeFanCommand(extruder_plan.getFanSpeed());
std::vector<GCodePath>& paths = extruder_plan.paths;
@@ -736,6 +727,12 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
GCodePath& path = paths[path_idx];
if (path.perform_prime)
{
gcode.writePrimeTrain(train->getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(retraction_config);
}
if (acceleration_enabled)
{
gcode.writeAcceleration(path.config->getAcceleration());
@@ -747,7 +744,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
if (path.retract)
{
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
if (path.perform_z_hop)
{
gcode.writeZhopStart(retraction_config.zHop);
@@ -866,7 +863,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
{
gcode.writeComment("Small layer, adding delay");
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
if (extruder_plan_idx == extruder_plans.size() - 1 || !train->getSettingBoolean("machine_extruder_end_pos_abs"))
{ // only move the head if it's the last extruder plan; otherwise it's already at the switching bay area
// or do it anyway when we switch extruder in-place
+30 -260
Ver Arquivo
@@ -6,9 +6,12 @@
#include "gcodeExport.h"
#include "pathPlanning/Comb.h"
#include "pathPlanning/GCodePath.h"
#include "pathPlanning/NozzleTempInsert.h"
#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"
@@ -20,252 +23,6 @@ namespace cura
{
class SliceDataStorage;
class SliceLayerPart;
/*!
* A gcode command to insert before a specific path.
*
* Currently only used for preheat commands
*/
struct NozzleTempInsert
{
const unsigned int path_idx; //!< The path before which to insert this command
double time_after_path_start; //!< The time after the start of the path, before which to insert the command // TODO: use this to insert command in between moves in a path!
int extruder; //!< The extruder for which to set the temp
double temperature; //!< The temperature of the temperature command to insert
bool wait; //!< Whether to wait for the temperature to be reached
NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start = 0.0)
: path_idx(path_idx)
, time_after_path_start(time_after_path_start)
, extruder(extruder)
, temperature(temperature)
, wait(wait)
{
assert(temperature != 0 && temperature != -1 && "Temperature command must be set!");
}
/*!
* Write the temperature command at the current position in the gcode.
* \param gcode The actual gcode writer
*/
void write(GCodeExport& gcode)
{
gcode.writeTemperatureCommand(extruder, temperature, wait);
}
};
class ExtruderPlan; // forward declaration so that TimeMaterialEstimates can be a friend
/*!
* Time and material estimates for a portion of paths, e.g. layer, extruder plan, path.
*/
class TimeMaterialEstimates
{
friend class ExtruderPlan; // cause there the naive estimates are calculated
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion)
double material; //!< Material used (in mm^3)
public:
/*!
* Basic contructor
*
* \param extrude_time Time in seconds occupied by extrusion
* \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion)
* \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion)
* \param material Material used (in mm^3)
*/
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material)
: extrude_time(extrude_time)
, unretracted_travel_time(unretracted_travel_time)
, retracted_travel_time(retracted_travel_time)
, material(material)
{
}
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
, retracted_travel_time(0.0)
, material(0.0)
{
}
/*!
* Set all estimates to zero.
*/
void reset()
{
extrude_time = 0.0;
unretracted_travel_time = 0.0;
retracted_travel_time = 0.0;
material = 0.0;
}
/*!
* Pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return The resulting estimates
*/
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
}
/*!
* In place pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return These estimates
*/
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other)
{
extrude_time += other.extrude_time;
unretracted_travel_time += other.unretracted_travel_time;
retracted_travel_time += other.retracted_travel_time;
material += other.material;
return *this;
}
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
*
* Each of the estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
* This causes the estimates in this instance to change. Each of the
* estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return A reference to this instance.
*/
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
/*!
* Get total time estimate. The different time estimate member values added together.
*
* \return the total of all different time estimate values
*/
double getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
/*!
* Get the total time during which the head is not retracted.
*
* This includes extrusion time and non-retracted travel time
*
* \return the total time during which the head is not retracted.
*/
double getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
/*!
* Get the total travel time.
*
* This includes the retracted travel time as well as the unretracted travel time.
*
* \return the total travel time.
*/
double getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
/*!
* Get the extrusion time.
*
* \return extrusion time.
*/
double getExtrudeTime() const
{
return extrude_time;
}
/*!
* Get the amount of material used in mm^3.
*
* \return amount of material
*/
double getMaterial() const
{
return material;
}
};
/*!
* A class for representing a planned path.
*
* A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc.
*
* This is a compact premature representation in which are line segments have the same config, i.e. the config of this path.
*
* In the final representation (gcode) each line segment may have different properties,
* which are added when the generated GCodePaths are processed.
*/
class GCodePath
{
public:
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.
bool perform_z_hop; //!< Whether to perform a z_hop in this path, which is assumed to be a travel path.
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
/*!
* Whether this config is the config of a travel path.
*
* \return Whether this config is the config of a travel path.
*/
bool isTravelPath()
{
return config->isTravelPath();
}
/*!
* Get the material flow in mm^3 per mm traversed.
*
* \warning Can only be called after the layer height has been set (which is done while writing the gcode!)
*
* \return The flow
*/
double getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
};
class GCodePlanner; // forward declaration so that ExtruderPlan can be a friend
class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend
@@ -475,8 +232,8 @@ private:
int last_extruder_previous_layer; //!< The last id of the extruder with which was printed in the previous layer
SettingsBaseVirtual* last_planned_extruder_setting_base; //!< The setting base of the last planned extruder.
SliceLayerPart* was_inside; //!< The layer part the last planned (extrusion) move was inside (if any)
SliceLayerPart* is_inside; //!< The layer part the destination of the next planned travel move is inside (if any)
bool was_inside; //!< Whether the last planned (extrusion) move was inside a layer part
bool is_inside; //!< Whether the destination of the next planned travel move is inside a layer part
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
Comb* comb;
@@ -517,7 +274,7 @@ public:
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_height, Point last_position, int current_extruder, 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);
GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_height, 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);
~GCodePlanner();
void overrideFanSpeeds(double speed);
@@ -565,10 +322,15 @@ public:
*
* Features like infill, walls, skin etc. are considered inside.
* Features like prime tower and support are considered outside.
* \param inside_part The part in which the newly planned position is inside, or nullptr if not inside anything
*/
void setIsInside(SliceLayerPart* inside_part);
void setIsInside(bool going_to_comb);
/*!
* Plan a switch to a new extruder
*
* \param extruder The extruder number to which to switch
* \return whether the extruder has changed
*/
bool setExtruder(int extruder);
/*!
@@ -599,6 +361,13 @@ public:
*/
GCodePath& addTravel_simple(Point p, GCodePath* path = nullptr);
/*!
* Plan a prime poop at the current location.
*
* \warning A nonretracted move is introduced so that the LayerPlanBuffer classifies this move as an extrusion move.
*/
void planPrime();
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
*
@@ -612,14 +381,15 @@ public:
/*!
* 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(PolygonRef polygon, int startIdx, 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.
@@ -632,12 +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(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, 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.
@@ -715,9 +486,8 @@ public:
* This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air.
*
* \param distance The distance to the comb boundary after we moved inside it.
* \param part_outline The part in which we last resided
*/
void moveInsideCombBoundary(int distance, const SliceLayerPart& part);
void moveInsideCombBoundary(int distance);
};
}//namespace cura
+30 -11
Ver Arquivo
@@ -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)
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
@@ -48,6 +48,14 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
break;
case EFillMethod::CUBICSUBDIV:
if (!mesh)
{
logError("Cannot generate Cubic Subdivision infill without a mesh!\n");
break;
}
generateCubicSubDivInfill(result_lines, *mesh);
break;
default:
logError("Fill pattern has unknown value.\n");
break;
@@ -58,7 +66,6 @@ void Infill::generateConcentricInfill(Polygons& result, int inset_value)
{
Polygons first_concentric_wall = in_outline.offset(outline_offset - line_distance + infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
result.add(first_concentric_wall);
if (perimeter_gaps)
{
const Polygons inner = first_concentric_wall.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
@@ -70,6 +77,7 @@ void Infill::generateConcentricInfill(Polygons& result, int inset_value)
void Infill::generateConcentricInfill(Polygons& first_concentric_wall, Polygons& result, int inset_value)
{
result.add(first_concentric_wall);
Polygons* prev_inset = &first_concentric_wall;
Polygons next_inset;
while (prev_inset->size() > 0)
@@ -79,7 +87,7 @@ void Infill::generateConcentricInfill(Polygons& first_concentric_wall, Polygons&
if (perimeter_gaps)
{
const Polygons outer = prev_inset->offset(-infill_line_width / 2 - perimeter_gaps_extra_offset);
const Polygons inner = next_inset.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
const Polygons inner = next_inset.offset(infill_line_width / 2);
const Polygons gaps_here = outer.difference(inner);
perimeter_gaps->add(gaps_here);
}
@@ -136,15 +144,26 @@ void Infill::generateTriangleInfill(Polygons& result)
generateLineInfill(result, line_distance, fill_angle + 120, 0);
}
void Infill::generateCubicSubDivInfill(Polygons& result, SliceMeshStorage& mesh)
{
Polygons uncropped;
mesh.base_subdiv_cube->generateSubdivisionLines(z, uncropped);
addLineSegmentsInfill(result, uncropped);
}
void Infill::addLineSegmentsInfill(Polygons& result, Polygons& input)
{
ClipperLib::PolyTree interior_segments_tree = in_outline.lineSegmentIntersection(input);
ClipperLib::Paths interior_segments;
ClipperLib::OpenPathsFromPolyTree(interior_segments_tree, interior_segments);
for (uint64_t idx = 0; idx < interior_segments.size(); idx++)
{
result.addLine(interior_segments[idx][0], interior_segments[idx][1]);
}
}
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t shift)
{
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(rotation_matrix.unapply(from));
p.add(rotation_matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
@@ -170,7 +189,7 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
{ // segment is too short to create infill
continue;
}
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
result.addLine(rotation_matrix.unapply(Point(x, crossings[crossing_idx])), rotation_matrix.unapply(Point(x, crossings[crossing_idx + 1])));
}
scanline_idx += 1;
}
+17 -1
Ver Arquivo
@@ -12,6 +12,7 @@
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "infill/SubDivCube.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
@@ -77,8 +78,9 @@ public:
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \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);
void generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh = nullptr);
private:
/*!
@@ -140,6 +142,13 @@ private:
* \param result (output) The resulting lines
*/
void generateTriangleInfill(Polygons& result);
/*!
* Generate a 3d pattern of subdivided cubes on their points
* \param[out] result The resulting lines
* \param[in] mesh Where the Cubic Subdivision Infill precomputation is stored
*/
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
@@ -153,6 +162,13 @@ private:
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t total_shift);
/*!
* Crop line segments by the infill polygon using Clipper
* \param result (output) The resulting lines
* \param input The line segments to be cropped
*/
void addLineSegmentsInfill(Polygons& result, Polygons& input);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
*
+282
Ver Arquivo
@@ -0,0 +1,282 @@
#include "SubDivCube.h"
#include <functional>
#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/math.h"
#define ONE_OVER_SQRT_2 0.7071067811865475244008443621048490392848359376884740 //1 / sqrt(2)
#define ONE_OVER_SQRT_3 0.577350269189625764509148780501957455647601751270126876018 //1 / sqrt(3)
#define ONE_OVER_SQRT_6 0.408248290463863016366214012450981898660991246776111688072 //1 / sqrt(6)
#define SQRT_TWO_THIRD 0.816496580927726032732428024901963797321982493552223376144 //sqrt(2 / 3)
namespace cura
{
std::vector<SubDivCube::CubeProperties> SubDivCube::cube_properties_per_recursion_step;
double SubDivCube::radius_multiplier = 1;
int32_t SubDivCube::radius_addition = 0;
Point3Matrix SubDivCube::rotation_matrix;
PointMatrix SubDivCube::infill_rotation_matrix;
SubDivCube::~SubDivCube()
{
for (int child_idx = 0; child_idx < 8; child_idx++)
{
if (children[child_idx])
{
delete children[child_idx];
}
}
}
void SubDivCube::precomputeOctree(SliceMeshStorage& mesh)
{
radius_multiplier = mesh.getSettingAsRatio("sub_div_rad_mult");
radius_addition = mesh.getSettingInMicrons("sub_div_rad_add");
double infill_angle = M_PI / 4.0;
coord_t furthest_dist_from_origin = std::sqrt(square(mesh.getSettingInMicrons("machine_height")) + square(mesh.getSettingInMicrons("machine_depth") / 2) + square(mesh.getSettingInMicrons("machine_width") / 2));
coord_t max_side_length = furthest_dist_from_origin * 2;
int curr_recursion_depth = 0;
const int64_t infill_line_distance = mesh.getSettingInMicrons("infill_line_distance");
if (infill_line_distance > 0)
{
for (int64_t curr_side_length = infill_line_distance * 2; curr_side_length < max_side_length * 2; curr_side_length *= 2)
{
cube_properties_per_recursion_step.emplace_back();
CubeProperties& cube_properties_here = cube_properties_per_recursion_step.back();
cube_properties_here.side_length = curr_side_length;
cube_properties_here.height = sqrt(3) * curr_side_length;
cube_properties_here.square_height = sqrt(2) * curr_side_length;
cube_properties_here.max_draw_z_diff = ONE_OVER_SQRT_3 * curr_side_length;
cube_properties_here.max_line_offset = ONE_OVER_SQRT_6 * curr_side_length;
curr_recursion_depth++;
}
}
Point3 center(0, 0, 0);
Point3Matrix tilt; // rotation matrix to get from axis aligned cubes to cubes standing on their tip
// The Z axis is transformed to go in positive Y direction
//
// cross section in a horizontal plane horizontal plane showing
// looking down at the origin O positive X and positive Y
// Z .
// /:\ Y .
// / : \ ^ .
// / : \ | .
// / .O. \ | .
// /.~' '~.\ O---->X .
// X """"""""""" Y .
tilt.matrix[0] = -ONE_OVER_SQRT_2; tilt.matrix[1] = ONE_OVER_SQRT_2; tilt.matrix[2] = 0;
tilt.matrix[3] = -ONE_OVER_SQRT_6; tilt.matrix[4] = -ONE_OVER_SQRT_6; tilt.matrix[5] = SQRT_TWO_THIRD ;
tilt.matrix[6] = ONE_OVER_SQRT_3; tilt.matrix[7] = ONE_OVER_SQRT_3; tilt.matrix[8] = ONE_OVER_SQRT_3;
infill_rotation_matrix = PointMatrix(infill_angle);
Point3Matrix infill_angle_mat(infill_rotation_matrix);
rotation_matrix = infill_angle_mat.compose(tilt);
mesh.base_subdiv_cube = new SubDivCube(mesh, center, curr_recursion_depth - 1);
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result)
{
if (cube_properties_per_recursion_step.empty()) //Infill is set to 0%.
{
return;
}
Polygons directional_line_groups[3];
generateSubdivisionLines(z, result, directional_line_groups);
for (int dir_idx = 0; dir_idx < 3; dir_idx++)
{
Polygons& line_group = directional_line_groups[dir_idx];
for (unsigned int line_idx = 0; line_idx < line_group.size(); line_idx++)
{
result.addLine(line_group[line_idx][0], line_group[line_idx][1]);
}
}
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3])
{
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
int32_t z_diff = std::abs(z - center.z); //!< the difference between the cube center and the target layer.
if (z_diff > cube_properties.height / 2) //!< this cube does not touch the target layer. Early exit.
{
return;
}
if (z_diff < cube_properties.max_draw_z_diff) //!< this cube has lines that need to be drawn.
{
Point relative_a, relative_b; //!< relative coordinates of line endpoints around cube center
Point a, b; //!< absolute coordinates of line endpoints
relative_a.X = (cube_properties.square_height / 2) * (cube_properties.max_draw_z_diff - z_diff) / cube_properties.max_draw_z_diff;
relative_b.X = -relative_a.X;
relative_a.Y = cube_properties.max_line_offset - ((z - (center.z - cube_properties.max_draw_z_diff)) * ONE_OVER_SQRT_2);
relative_b.Y = relative_a.Y;
rotatePointInitial(relative_a);
rotatePointInitial(relative_b);
for (int dir_idx = 0; dir_idx < 3; dir_idx++)//!< draw the line, then rotate 120 degrees.
{
a.X = center.x + relative_a.X;
a.Y = center.y + relative_a.Y;
b.X = center.x + relative_b.X;
b.Y = center.y + relative_b.Y;
addLineAndCombine(directional_line_groups[dir_idx], a, b);
if (dir_idx < 2)
{
rotatePoint120(relative_a);
rotatePoint120(relative_b);
}
}
}
for (int idx = 0; idx < 8; idx++) //!< draws the eight children
{
if (children[idx] != nullptr)
{
children[idx]->generateSubdivisionLines(z, result, directional_line_groups);
}
}
}
SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth)
{
this->depth = depth;
this->center = center;
if (depth == 0) // lowest layer, no need for subdivision, exit.
{
return;
}
if (depth >= cube_properties_per_recursion_step.size()) //Depth is out of bounds of what we pre-computed.
{
return;
}
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
Point3 child_center;
coord_t radius = double(radius_multiplier * double(cube_properties.height)) / 4.0 + radius_addition;
int child_nr = 0;
std::vector<Point3> rel_child_centers;
rel_child_centers.emplace_back(1, 1, 1); // top
rel_child_centers.emplace_back(-1, 1, 1); // top three
rel_child_centers.emplace_back(1, -1, 1);
rel_child_centers.emplace_back(1, 1, -1);
rel_child_centers.emplace_back(-1, -1, -1); // bottom
rel_child_centers.emplace_back(1, -1, -1); // bottom three
rel_child_centers.emplace_back(-1, 1, -1);
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 * coord_t(cube_properties.side_length / 4));
if (isValidSubdivision(mesh, child_center, radius))
{
children[child_nr] = new SubDivCube(mesh, child_center, depth - 1);
child_nr++;
}
}
}
bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius)
{
int64_t distance2;
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 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));
Point loc(center.x, center.y);
inside = distanceFromPointToMesh(mesh, test_layer, loc, &distance2);
if (inside == 1)
{
inside_somewhere = true;
}
else
{
outside_somewhere = true;
}
if (outside_somewhere && inside_somewhere)
{
return true;
}
if ((inside != 2) && distance2 < sphere_slice_radius2)
{
return true;
}
}
return false;
}
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, long int layer_nr, Point& location, int64_t* distance2)
{
if (layer_nr < 0 || (unsigned long int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
{
return 2;
}
Polygons collide;
mesh.layers[layer_nr].getSecondOrInnermostWalls(collide);
Point centerpoint = location;
bool inside = collide.inside(centerpoint);
ClosestPolygonPoint border_point = PolygonUtils::moveInside2(collide, centerpoint);
Point diff = border_point.location - location;
*distance2 = vSize2(diff);
if (inside)
{
return 1;
}
return 0;
}
void SubDivCube::rotatePointInitial(Point& target)
{
target = infill_rotation_matrix.apply(target);
}
void SubDivCube::rotatePoint120(Point& target)
{
constexpr double sqrt_three_fourths = 0.8660254037844386467637231707529361834714026269051903; //!< sqrt(3.0 / 4.0) = sqrt(3) / 2
int64_t x;
x = (-0.5) * target.X - sqrt_three_fourths * target.Y;
target.Y = (-0.5)*target.Y + sqrt_three_fourths * target.X;
target.X = x;
}
void SubDivCube::addLineAndCombine(Polygons& group, Point from, Point to)
{
int epsilon = 10; // the smallest distance of two points which are viewed as coincident (dist > 0 due to rounding errors)
for (unsigned int idx = 0; idx < group.size(); idx++)
{
if (std::abs(from.X - group[idx][1].X) < epsilon && std::abs(from.Y - group[idx][1].Y) < epsilon)
{
from = group[idx][0];
group.remove(idx);
idx--;
continue;
}
if (std::abs(to.X - group[idx][0].X) < epsilon && std::abs(to.Y - group[idx][0].Y) < epsilon)
{
to = group[idx][1];
group.remove(idx);
idx--;
continue;
}
}
group.addLine(from, to);
}
}//namespace cura
+98
Ver Arquivo
@@ -0,0 +1,98 @@
#ifndef INFILL_SUBDIVCUBE_H
#define INFILL_SUBDIVCUBE_H
#include "../sliceDataStorage.h"
namespace cura
{
class Infill;
class SubDivCube
{
public:
/*!
* Constructor for SubDivCube. Recursively calls itself eight times to flesh out the octree.
* \param mesh contains infill layer data and settings
* \param my_center the center of the cube
* \param depth the recursion depth of the cube (0 is most recursed)
*/
SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth);
~SubDivCube(); //!< destructor (also destroys children
/*!
* Precompute the octree of subdivided cubes
* \param mesh contains infill layer data and settings
*/
static void precomputeOctree(SliceMeshStorage& mesh);
/*!
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
*/
void generateSubdivisionLines(int64_t z, Polygons& result);
private:
/*!
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
* \param directional_line_groups Array of 3 times a polylines. Used to keep track of line segments that are all pointing the same direction for line segment combining
*/
void generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3]);
struct CubeProperties
{
int64_t side_length; //!< side length of cubes
int64_t height; //!< height of cubes based. This is the distance from one point of a cube to its 3d opposite.
int64_t square_height; //!< square cut across lengths. This is the diagonal distance across a face of the cube.
int64_t max_draw_z_diff; //!< maximum draw z differences. This is the maximum difference in z at which lines need to be drawn.
int64_t max_line_offset; //!< maximum line offsets. This is the maximum distance at which subdivision lines should be drawn from the 2d cube center.
};
/*!
* Rotates a point 120 degrees about the origin.
* \param target the point to rotate.
*/
static void rotatePoint120(Point& target);
/*!
* Rotates a point to align it with the orientation of the infill.
* \param target the point to rotate.
*/
static void rotatePointInitial(Point& target);
/*!
* Determines if a described theoretical cube should be subdivided based on if a sphere that encloses the cube touches the infill mesh.
* \param mesh contains infill layer data and settings
* \param center the center of the described cube
* \param radius the radius of the enclosing sphere
* \return the described cube should be subdivided
*/
static bool isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius);
/*!
* Finds the distance to the infill border at the specified layer from the specified point.
* \param mesh contains infill layer data and settings
* \param layer_nr the number of the specified layer
* \param location the location of the specified point
* \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, 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.
* \param[out] group the polygons to add the line to
* \param from the first endpoint of the line
* \param to the second endpoint of the line
*/
void addLineAndCombine(Polygons& group, Point from, Point to);
unsigned int depth; //!< the recursion depth of the cube (0 is most recursed)
Point3 center; //!< center location of the cube in absolute coordinates
SubDivCube* children[8] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; //!< pointers to this cube's eight octree children
static std::vector<CubeProperties> cube_properties_per_recursion_step; //!< precomputed array of basic properties of cubes based on recursion depth.
static double radius_multiplier; //!< multiplier for the bounding radius when determining if a cube should be subdivided
static Point3Matrix rotation_matrix; //!< The rotation matrix to get from axis aligned cubes to cubes standing on a corner point aligned with the infill_angle
static PointMatrix infill_rotation_matrix; //!< Horizontal rotation applied to infill
static int32_t radius_addition; //!< addition to the bounding radius when determining if a cube should be subdivided
};
}
#endif //INFILL_SUBDIVCUBE_H
+1 -3
Ver Arquivo
@@ -109,9 +109,7 @@ protected:
*/
void addLine(Point from, Point to)
{
PolygonRef line_poly = result.newPoly();
line_poly.add(rotation_matrix.unapply(from));
line_poly.add(rotation_matrix.unapply(to));
result.addLine(rotation_matrix.unapply(from), rotation_matrix.unapply(to));
}
/*!
+2 -1
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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 -19
Ver Arquivo
@@ -92,7 +92,7 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
switch (type)
{
case EZSeamType::BACK: return getFarthestPointInPolygon(poly_idx);
case EZSeamType::BACK: return getClosestPointInPolygon(z_seam_pos, poly_idx);
case EZSeamType::RANDOM: return getRandomPointInPolygon(poly_idx);
case EZSeamType::SHORTEST: return getClosestPointInPolygon(prev_point, poly_idx);
default: return getClosestPointInPolygon(prev_point, poly_idx);
@@ -129,24 +129,6 @@ int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
return rand() % polygons[poly_idx].size();
}
int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float best_y = std::numeric_limits<float>::min();
for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
{
if (poly[point_idx].Y > best_y)
{
best_point_idx = point_idx;
best_y = poly[point_idx].Y;
}
}
return best_point_idx;
}
/**
*
*/
+10 -2
Ver Arquivo
@@ -19,13 +19,15 @@ class PathOrderOptimizer
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<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
PathOrderOptimizer(Point startPoint, EZSeamType type = EZSeamType::SHORTEST)
PathOrderOptimizer(Point startPoint, Point z_seam_pos = Point(0, 0), EZSeamType type = EZSeamType::SHORTEST)
: type(type)
, startPoint(startPoint)
, z_seam_pos(z_seam_pos)
{
}
@@ -43,9 +45,15 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
/*!
* Get the starting vertex of a polygon, depending on the \ref PathOrderOptimizer::type
* \param prev_point The previous planned location
* \param poly_idx The index of the polygon in \ref PathOrderOptimizer::polygons
* \return the index of the starting vertex in \ref PathOrderOptimizer::polygons[\p poly_idx]
*/
int getPolyStart(Point prev_point, int poly_idx);
int getClosestPointInPolygon(Point prev, int i_polygon); //!< returns the index of the closest point
int getFarthestPointInPolygon(int poly_idx); //!< return the index to the point farthest from the front (highest y)
int getRandomPointInPolygon(int poly_idx);
+6 -35
Ver Arquivo
@@ -8,23 +8,10 @@
#include "../utils/polygonUtils.h"
#include "../utils/PolygonsPointIndex.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"
#include "../utils/linearAlg2D.h"
namespace cura {
Polygons Comb::getCombOutlines()
{
if (layer_nr >= 0)
{
bool include_helper_parts = false;
return storage.getLayerOutlines(layer_nr, include_helper_parts);
}
else
{
return storage.raftOutline;
}
}
LocToLineGrid& Comb::getOutsideLocToLine()
{
return *outside_loc_to_line;
@@ -39,14 +26,13 @@ Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_insi
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_move_inside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, offset_from_inside_to_outside(offset_from_outlines + offset_from_outlines_outside)
, max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation
, avoid_other_parts(travel_avoid_other_parts)
, boundary_inside( comb_boundary_inside )
, partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !!
, outlines(getCombOutlines())
, inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset))
, boundary_outside(
[&storage, layer_nr, travel_avoid_distance]()
@@ -235,30 +221,15 @@ bool Comb::moveInside(bool is_inside, Point& dest_point, unsigned int& inside_po
{
if (is_inside)
{
coord_t max_move_inside_distance2_here = std::numeric_limits<coord_t>::max(); // the distance which would make the moveInside fail
if (storage.getSettingAsCombingMode("retraction_combing") == cura::CombingMode::NO_SKIN)
{ // if we perform no_skin combing, then a far move inside is likely a consequence of there meing skin in between the destination point and the inside comb boundary
// if we perform normal combing, then a far move inside is likely to be a consequence of sharp pointy segments in the layer part
max_move_inside_distance2_here = max_move_inside_distance2;
}
Point original_dest_point = dest_point;
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_move_inside_distance2_here, &boundary_inside, inside_loc_to_line);
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_moveInside_distance2, &boundary_inside, inside_loc_to_line);
if (!cpp.isValid())
{
return false;
}
else
{
if (vSize2(dest_point - original_dest_point) > max_move_inside_distance2 // only check for collision with outlines for long moves
&& PolygonUtils::polygonCollidesWithLineSegment(outlines, dest_point, original_dest_point))
{
return false;
}
else
{
inside_poly = cpp.poly_idx;
return true;
}
inside_poly = cpp.poly_idx;
return true;
}
}
return false;
@@ -274,7 +245,7 @@ void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, cons
dest_part = partsView_inside.assemblePart(dest_part_idx);
ClosestPolygonPoint boundary_crossing_point;
{ // set [result] to a point on the destination part closest to close_to (but also a bit close to fest_point)
{ // set [result] to a point on the destination part closest to close_to (but also a bit close to _dest_point)
std::unordered_set<unsigned int> dest_part_poly_indices;
for (unsigned int poly_idx : partsView_inside[dest_part_idx])
{
+2 -8
Ver Arquivo
@@ -105,7 +105,7 @@ private:
const int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
const int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
const int64_t max_move_inside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
const int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
const int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
const int64_t offset_from_inside_to_outside; //!< The sum of the offsets for the inside and outside boundary Comb::offset_from_outlines and Comb::offset_from_outlines_outside
const int64_t max_crossing_dist2; //!< The maximal distance by which to cross the in_between area between inside and outside
@@ -114,19 +114,13 @@ private:
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
const bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
Polygons& boundary_inside; //!< The boundary within which to comb.
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
Polygons outlines; //!< The actual boundary between the model and air
LocToLineGrid* inside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the inner boundary.
LazyInitialization<Polygons> boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
LazyInitialization<LocToLineGrid, Comb*, const int64_t> outside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the outside boundary.
/*!
* Get the outlines of the meshes or raft for this layer
*/
Polygons getCombOutlines();
/*!
* Get the SparsePointGridInclusive mapping locations to line segments of the outside boundary. Calculate it when it hasn't been calculated yet.
*/
+24
Ver Arquivo
@@ -0,0 +1,24 @@
//Copyright (C) 2016 Ultimaker
//Released under terms of the AGPLv3 License
#include "GCodePath.h"
namespace cura
{
bool GCodePath::isTravelPath()
{
return config->isTravelPath();
}
double GCodePath::getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
int GCodePath::getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
}
+64
Ver Arquivo
@@ -0,0 +1,64 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_G_CODE_PATH_H
#define PATH_PLANNING_G_CODE_PATH_H
#include "../SpaceFillType.h"
#include "../GCodePathConfig.h"
#include "TimeMaterialEstimates.h"
namespace cura
{
/*!
* A class for representing a planned path.
*
* A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc.
*
* This is a compact premature representation in which are line segments have the same config, i.e. the config of this path.
*
* In the final representation (gcode) each line segment may have different properties,
* which are added when the generated GCodePaths are processed.
*/
class GCodePath
{
public:
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.
bool perform_z_hop; //!< Whether to perform a z_hop in this path, which is assumed to be a travel path.
bool perform_prime; //!< Whether this path is preceded by a prime (poop)
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
/*!
* Whether this config is the config of a travel path.
*
* \return Whether this config is the config of a travel path.
*/
bool isTravelPath();
/*!
* Get the material flow in mm^3 per mm traversed.
*
* \warning Can only be called after the layer height has been set (which is done while writing the gcode!)
*
* \return The flow
*/
double getExtrusionMM3perMM();
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth();
};
}//namespace cura
#endif//PATH_PLANNING_G_CODE_PATH_H
+24
Ver Arquivo
@@ -0,0 +1,24 @@
//Copyright (C) 2016 Ultimaker
//Released under terms of the AGPLv3 License
#include "NozzleTempInsert.h"
namespace cura
{
NozzleTempInsert::NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start)
: path_idx(path_idx)
, time_after_path_start(time_after_path_start)
, extruder(extruder)
, temperature(temperature)
, wait(wait)
{
assert(temperature != 0 && temperature != -1 && "Temperature command must be set!");
}
void NozzleTempInsert::write(GCodeExport& gcode)
{
gcode.writeTemperatureCommand(extruder, temperature, wait);
}
}
+32
Ver Arquivo
@@ -0,0 +1,32 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_NOZZLE_TEMP_INSERT_H
#define PATH_PLANNING_NOZZLE_TEMP_INSERT_H
#include "../gcodeExport.h"
namespace cura
{
/*!
* A gcode command to insert before a specific path.
*
* Currently only used for preheat commands
*/
struct NozzleTempInsert
{
const unsigned int path_idx; //!< The path before which to insert this command
double time_after_path_start; //!< The time after the start of the path, before which to insert the command // TODO: use this to insert command in between moves in a path!
int extruder; //!< The extruder for which to set the temp
double temperature; //!< The temperature of the temperature command to insert
bool wait; //!< Whether to wait for the temperature to be reached
NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start = 0.0);
/*!
* Write the temperature command at the current position in the gcode.
* \param gcode The actual gcode writer
*/
void write(GCodeExport& gcode);
};
}//namespace cura
#endif//PATH_PLANNING_NOZZLE_TEMP_INSERT_H
+84
Ver Arquivo
@@ -0,0 +1,84 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "TimeMaterialEstimates.h"
namespace cura
{
TimeMaterialEstimates::TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material)
: extrude_time(extrude_time)
, unretracted_travel_time(unretracted_travel_time)
, retracted_travel_time(retracted_travel_time)
, material(material)
{
}
TimeMaterialEstimates::TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
, retracted_travel_time(0.0)
, material(0.0)
{
}
TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other)
{
extrude_time -= other.extrude_time;
unretracted_travel_time -= other.unretracted_travel_time;
retracted_travel_time -= other.retracted_travel_time;
material -= other.material;
return *this;
}
TimeMaterialEstimates TimeMaterialEstimates::operator+(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator+=(const TimeMaterialEstimates& other)
{
extrude_time += other.extrude_time;
unretracted_travel_time += other.unretracted_travel_time;
retracted_travel_time += other.retracted_travel_time;
material += other.material;
return *this;
}
double TimeMaterialEstimates::getExtrudeTime() const
{
return extrude_time;
}
double TimeMaterialEstimates::getMaterial() const
{
return material;
}
double TimeMaterialEstimates::getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
double TimeMaterialEstimates::getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
double TimeMaterialEstimates::getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
void TimeMaterialEstimates::reset()
{
extrude_time = 0.0;
unretracted_travel_time = 0.0;
retracted_travel_time = 0.0;
material = 0.0;
}
}//namespace cura
+124
Ver Arquivo
@@ -0,0 +1,124 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
#define PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
#include "../gcodeExport.h"
namespace cura
{
class ExtruderPlan; // forward declaration so that TimeMaterialEstimates can be a friend
/*!
* Time and material estimates for a portion of paths, e.g. layer, extruder plan, path.
*/
class TimeMaterialEstimates
{
friend class ExtruderPlan; // cause there the naive estimates are calculated
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion)
double material; //!< Material used (in mm^3)
public:
/*!
* Basic contructor
*
* \param extrude_time Time in seconds occupied by extrusion
* \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion)
* \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion)
* \param material Material used (in mm^3)
*/
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material);
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates();
/*!
* Set all estimates to zero.
*/
void reset();
/*!
* Pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return The resulting estimates
*/
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other);
/*!
* In place pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return These estimates
*/
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
*
* Each of the estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
* This causes the estimates in this instance to change. Each of the
* estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return A reference to this instance.
*/
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
/*!
* Get total time estimate. The different time estimate member values added together.
*
* \return the total of all different time estimate values
*/
double getTotalTime() const;
/*!
* Get the total time during which the head is not retracted.
*
* This includes extrusion time and non-retracted travel time
*
* \return the total time during which the head is not retracted.
*/
double getTotalUnretractedTime() const;
/*!
* Get the total travel time.
*
* This includes the retracted travel time as well as the unretracted travel time.
*
* \return the total travel time.
*/
double getTravelTime() const;
/*!
* Get the extrusion time.
*
* \return extrusion time.
*/
double getExtrudeTime() const;
/*!
* Get the amount of material used in mm^3.
*
* \return amount of material
*/
double getMaterial() const;
};
}//namespace cura
#endif//PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
+1 -1
Ver Arquivo
@@ -43,7 +43,7 @@ int Raft::getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage)
{
return 0;
}
const int64_t airgap = std::max(0, train.getSettingInMicrons("raft_airgap"));
const int64_t airgap = std::max((coord_t)0, train.getSettingInMicrons("raft_airgap"));
const int64_t layer_0_overlap = storage.getSettingInMicrons("layer_0_z_overlap");
const int64_t layer_height_0 = storage.getSettingInMicrons("layer_height_0");
+10 -8
Ver Arquivo
@@ -125,6 +125,11 @@ 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()) //... Tough luck, buddy.
{
logError("Couldn't find any extruder trains!\n");
return -1;
}
if (extruder_nr >= extruder_train_ids.size())
{
logWarning("Couldn't load extruder.def.json file for extruder %i. Index out of bounds.\n Loading first extruder definition instead.\n", extruder_nr);
@@ -220,8 +225,7 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
if (json_document.HasMember("settings"))
{
std::list<std::string> path;
handleChildren(json_document["settings"], path, settings_base, warn_duplicates);
handleChildren(json_document["settings"], settings_base, warn_duplicates);
}
if (json_document.HasMember("overrides"))
@@ -243,7 +247,7 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
return 0;
}
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates)
{
if (!settings_list.IsObject())
{
@@ -252,12 +256,10 @@ void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std:
}
for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
{
handleSetting(setting_iterator, path, settings_base, warn_duplicates);
handleSetting(setting_iterator, settings_base, warn_duplicates);
if (setting_iterator->value.HasMember("children"))
{
std::list<std::string> path_here = path;
path_here.push_back(setting_iterator->name.GetString());
handleChildren(setting_iterator->value["children"], path_here, settings_base, warn_duplicates);
handleChildren(setting_iterator->value["children"], settings_base, warn_duplicates);
}
}
}
@@ -275,7 +277,7 @@ bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
}
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates)
{
const rapidjson::Value& json_setting = json_setting_it->value;
if (!json_setting.IsObject())
+2 -3
Ver Arquivo
@@ -174,17 +174,16 @@ private:
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
void handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Handle a json object for a setting.
*
* \param json_setting_it Iterator for the setting which contains the key (setting name) and attributes info
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates);
};
}//namespace cura
+37 -3
Ver Arquivo
@@ -156,7 +156,7 @@ double SettingsBaseVirtual::getSettingInMillimeters(std::string key) const
return atof(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
coord_t SettingsBaseVirtual::getSettingInMicrons(std::string key) const
{
return getSettingInMillimeters(key) * 1000.0;
}
@@ -210,6 +210,12 @@ double SettingsBaseVirtual::getSettingInPercentage(std::string key) const
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingAsRatio(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 100.0;
}
double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
{
std::string value = getSettingString(key);
@@ -345,6 +351,8 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
return EFillMethod::GRID;
if (value == "cubic")
return EFillMethod::CUBIC;
if (value == "cubicsubdiv")
return EFillMethod::CUBICSUBDIV;
if (value == "tetrahedral")
return EFillMethod::TETRAHEDRAL;
if (value == "triangles")
@@ -418,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")
@@ -436,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")
@@ -450,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
+18 -3
Ver Arquivo
@@ -105,6 +105,7 @@ enum class EFillMethod
LINES,
GRID,
CUBIC,
CUBICSUBDIV,
TETRAHEDRAL,
TRIANGLES,
CONCENTRIC,
@@ -176,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.
@@ -230,12 +243,13 @@ public:
double getSettingInAngleDegrees(std::string key) const;
double getSettingInAngleRadians(std::string key) const;
double getSettingInMillimeters(std::string key) const;
int getSettingInMicrons(std::string key) const;
coord_t getSettingInMicrons(std::string key) const;
bool getSettingBoolean(std::string key) const;
double getSettingInDegreeCelsius(std::string key) const;
double getSettingInMillimetersPerSecond(std::string key) const;
double getSettingInCubicMillimeters(std::string key) const;
double getSettingInPercentage(std::string key) const;
double getSettingAsRatio(std::string key) const; //!< For settings which are provided in percentage
double getSettingInSeconds(std::string key) const;
FlowTempGraph getSettingAsFlowTempGraph(std::string key) const;
@@ -249,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;
+1
Ver Arquivo
@@ -146,6 +146,7 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wal
int extra_offset = 0;
EFillMethod fill_pattern = mesh.getSettingAsFillMethod("infill_pattern");
if ((fill_pattern == EFillMethod::CONCENTRIC || fill_pattern == EFillMethod::CONCENTRIC_3D)
&& mesh.getSettingBoolean("alternate_extra_perimeter")
&& layerNr % 2 == 0
&& mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") * 2)
{
+59 -27
Ver Arquivo
@@ -1,6 +1,11 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "sliceDataStorage.h"
#include "FffProcessor.h" //To create a mesh group with if none is provided.
#include "infill/SubDivCube.h" // For the destructor
namespace cura
{
@@ -67,6 +72,48 @@ 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()
{
std::vector<RetractionConfig> ret;
@@ -106,37 +153,16 @@ SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(mes
raft_surface_config(PrintFeatureType::SupportInterface),
support_config(PrintFeatureType::Support),
support_skin_config(PrintFeatureType::SupportInterface),
max_print_height_second_to_last_extruder(-1)
max_print_height_second_to_last_extruder(-1),
primeTower(*this)
{
}
SliceLayerPart* SliceDataStorage::getPartInside(int layer_nr, Point location)
SliceDataStorage::~SliceDataStorage()
{
if (layer_nr >= 0)
{
for (SliceMeshStorage& mesh : meshes)
{
if ((unsigned int)layer_nr >= mesh.layers.size())
{
continue;
}
SliceLayer& layer = mesh.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{
if (part.outline.inside(location))
{
return &part;
}
}
}
return nullptr;
}
else
{
return nullptr;
}
}
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
{
if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
@@ -189,7 +215,10 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
if (primeTower.enabled)
{
total.add(primeTower.ground_poly);
}
}
return total;
}
@@ -230,7 +259,10 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
if (primeTower.enabled)
{
total.add(primeTower.ground_poly);
}
}
return total;
}
+23 -25
Ver Arquivo
@@ -12,6 +12,7 @@
#include "MeshGroup.h"
#include "PrimeTower.h"
#include "GCodePathConfig.h"
#include "textureProcessing/TextureProximityProcessor.h"
namespace cura
{
@@ -143,7 +144,15 @@ public:
};
/******************/
class SliceMeshStorage : public SettingsMessenger // passes on settings from a Mesh object
class SubDivCube; // forward declaration to prevent dependency loop
/*!
*
* 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,18 +164,18 @@ public:
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
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)
{
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);
}
SubDivCube* base_subdiv_cube;
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();
};
class SliceDataStorage : public SettingsMessenger, NoCopy
@@ -235,18 +244,7 @@ public:
*/
SliceDataStorage(MeshGroup* meshgroup);
~SliceDataStorage()
{
}
/*!
* Check in which part \p location lies, if in any.
*
* \param layer_nr The layer for which to check
* \param location The location to check
* \return The part in which \p location lie, or nullptr, if it's outside all parts.
*/
SliceLayerPart* getPartInside(int layer_nr, Point location);
~SliceDataStorage();
/*!
* Get all outlines within a given layer.
+21
Ver Arquivo
@@ -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
+22
Ver Arquivo
@@ -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
+4 -4
Ver Arquivo
@@ -1,10 +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 "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.
+6 -6
Ver Arquivo
@@ -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
+180
Ver Arquivo
@@ -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
+75
Ver Arquivo
@@ -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
+26 -122
Ver Arquivo
@@ -1,21 +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"
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++)
@@ -759,7 +767,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
for (PolygonRef polyline : open_polylines)
{
if (polyline.size() > 0)
openPolylines.add(polyline);
polygons.add(polyline);
}
}
@@ -774,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();
@@ -786,113 +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());
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());
}
}//namespace cura
} // namespace cura
+30 -80
Ver Arquivo
@@ -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
+59
Ver Arquivo
@@ -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
+22 -5
Ver Arquivo
@@ -1,4 +1,7 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
//Copyright (C) 2013 David Braam
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include <cmath> // sqrt
#include <utility> // pair
#include <deque>
@@ -274,8 +277,8 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight;
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false));
}
supportLayer_last = supportLayer_this;
@@ -295,7 +298,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
}
else
{
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
supportLayer_this = supportLayer_this.difference(outlines.offset(supportXYDistance));
}
}
@@ -338,6 +341,17 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
}
}
//Enforce top Z distance.
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; layer_idx++)
{
supportAreas[layer_idx] = supportAreas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layerZdistanceTop, false));
}
}
for (unsigned int layer_idx = supportAreas.size() - 1; layer_idx != (unsigned int) std::max(-1, storage.support.layer_nr_max_filled_layer) ; layer_idx--)
{
@@ -373,7 +387,10 @@ std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const Sli
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
const SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
basic_overhang = basic_overhang.difference(support_layer.anti_overhang);
if (support_layer.anti_overhang.size())
{
basic_overhang = basic_overhang.difference(support_layer.anti_overhang);
}
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
// support_extension = support_extension.intersection(supportLayer_supported);
+59
Ver Arquivo
@@ -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
+42
Ver Arquivo
@@ -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
+44
Ver Arquivo
@@ -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
+27
Ver Arquivo
@@ -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
+178
Ver Arquivo
@@ -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
+71
Ver Arquivo
@@ -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
+42
Ver Arquivo
@@ -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
+27
Ver Arquivo
@@ -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
+144
Ver Arquivo
@@ -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
+77
Ver Arquivo
@@ -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
+1 -1
Ver Arquivo
@@ -57,7 +57,7 @@ public:
};
private:
double max_feedrate[NUM_AXIS] = {600, 600, 40, 25};
double max_feedrate[NUM_AXIS] = {600, 600, 40, 25}; // mm/s
double minimumfeedrate = 0.01;
double acceleration = 3000;
double max_acceleration[NUM_AXIS] = {9000, 9000, 100, 10000};
+74
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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(PolygonRef poly, ListPolygon& result)
void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result, bool remove_duplicates)
{
#ifdef DEBUG
Point last = poly.back();
#endif // DEBUG
for (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);
}
}
}
+8 -4
Ver Arquivo
@@ -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(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.
+2 -1
Ver Arquivo
@@ -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();
+68 -6
Ver Arquivo
@@ -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,
+1 -3
Ver Arquivo
@@ -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);
}
+89 -21
Ver Arquivo
@@ -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); }
@@ -285,6 +293,66 @@ public:
}
};
class Point3Matrix
{
public:
double matrix[9];
Point3Matrix()
{
matrix[0] = 1;
matrix[1] = 0;
matrix[2] = 0;
matrix[3] = 0;
matrix[4] = 1;
matrix[5] = 0;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 1;
}
/*!
* Initializes the top left corner with the values of \p b
* and the rest as if it's a unit matrix
*/
Point3Matrix(const PointMatrix& b)
{
matrix[0] = b.matrix[0];
matrix[1] = b.matrix[1];
matrix[2] = 0;
matrix[3] = b.matrix[2];
matrix[4] = b.matrix[3];
matrix[5] = 0;
matrix[6] = 0;
matrix[7] = 0;
matrix[8] = 1;
}
Point3 apply(const Point3 p) const
{
return Point3(p.x * matrix[0] + p.y * matrix[1] + p.z * matrix[2]
, p.x * matrix[3] + p.y * matrix[4] + p.z * matrix[5]
, p.x * matrix[6] + p.y * matrix[7] + p.z * matrix[8]);
}
Point3Matrix compose(const Point3Matrix& b)
{
Point3Matrix ret;
for (int outx = 0; outx < 3; outx++)
{
for (int outy = 0; outy < 3; outy++)
{
ret.matrix[outy * 3 + outx] = 0;
for (int in = 0; in < 3; in++)
{
ret.matrix[outy * 3 + outx] += matrix[outy * 3 + in] * b.matrix[in * 3 + outx];
}
}
}
return ret;
}
};
inline Point3 operator+(const Point3& p3, const Point& p2) {
return Point3(p3.x + p2.X, p3.y + p2.Y, p3.z);
+1 -1
Ver Arquivo
@@ -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
}
+4 -4
Ver Arquivo
@@ -45,7 +45,7 @@ public:
other.instance = nullptr;
}
template<class... Args>
constexpr explicit optional(bool not_used, Args&&... args ) //!< construct the value in place
constexpr explicit optional(bool, Args&&... args ) //!< construct the value in place
: instance(new T(args...))
{
}
@@ -62,7 +62,7 @@ public:
* \param null_ptr exactly [nullptr]
* \return this
*/
optional& operator=(std::nullptr_t null_ptr)
optional& operator=(std::nullptr_t)
{
if (instance)
{
@@ -121,12 +121,12 @@ public:
}
constexpr T* operator->() const
{
assert(instance && "instance should be instatiated!");
assert(instance && "Instance should be instantiated!");
return instance;
}
constexpr T& operator*() const&
{
assert(instance && "instance should be instatiated!");
assert(instance && "Instance should be instantiated!");
return *instance;
}
constexpr explicit operator bool() const
+99
Ver Arquivo
@@ -0,0 +1,99 @@
/** Copyright (C) 2016 Ultimaker B.V. - Released under terms of the AGPLv3 License */
#ifndef UTILS_ORDER_OPTIMIZER_H
#define UTILS_ORDER_OPTIMIZER_H
#include <stdint.h>
#include <vector>
#include <list>
#include <utility> // pair
#include "intpoint.h"
namespace cura {
/*!
* Order optimization class.
*
* Utility class for optimizing the path order by minimizing the cyclic distance traveled between several items.
*
* The path is heuristically optimized in a way such that each node is visited and the salesman which is travelling ends up where he started.
*/
template <typename T>
class OrderOptimizer
{
public:
std::vector<std::pair<const Point, T>> items; //!< the items in arbitrary order
OrderOptimizer()
{
}
void addItem(const Point location, const T item);
/*!
* Optimize the order of \ref OrderOptimizer::items
* \return A vector of the ordered indices into \ref OrderOptimizer::items
*/
std::list<unsigned int> optimize();
};
template <typename T>
void OrderOptimizer<T>::addItem(const Point location, const T item)
{
this->items.emplace_back(location, item);
}
template <typename T>
std::list<unsigned int> OrderOptimizer<T>::optimize()
{
// least detour insertion algorithm
std::list<unsigned int> order;
if (items.size() == 0)
{
return order;
}
order.push_back(0u);
if (items.size() == 1)
{
return order;
}
order.push_back(1u);
if (items.size() == 2)
{
return order;
}
order.push_back(2u);
for (unsigned int item_idx = 3; item_idx < items.size(); item_idx++)
{
Point to_insert_item_location = items[item_idx].first;
// find best_item_to_insert_before
std::list<unsigned int>::iterator best_item_to_insert_before = order.begin();
coord_t best_detour_dist = vSize(items[*best_item_to_insert_before].first - to_insert_item_location)
+ vSize(to_insert_item_location - items[order.back()].first)
- vSize(items[*best_item_to_insert_before].first - items[order.back()].first);
std::list<unsigned int>::iterator prev = order.begin();
for (std::list<unsigned int>::iterator nearby = ++order.begin(); nearby != order.end(); ++nearby)
{
coord_t detour_dist = vSize(items[*nearby].first - to_insert_item_location)
+ vSize(to_insert_item_location - items[*prev].first)
- vSize(items[*nearby].first - items[*prev].first);
if (detour_dist < best_detour_dist)
{
best_detour_dist = detour_dist;
best_item_to_insert_before = nearby;
}
prev = nearby;
}
order.insert(best_item_to_insert_before, item_idx);
}
return order;
}
}//namespace cura
#endif//UTILS_ORDER_OPTIMIZER_H
+10 -21
Ver Arquivo
@@ -97,27 +97,6 @@ bool Polygons::inside(Point p, bool border_result) const
return (poly_count_inside % 2) == 1;
}
bool PolygonsPart::inside(Point p, bool border_result) const
{
if (size() < 1)
{
return false;
}
if (!(*this)[0].inside(p, border_result))
{
return false;
}
for (unsigned int n = 1; n < paths.size(); n++)
{
if ((*this)[n].inside(p, !border_result))
{
return false;
}
}
return true;
}
bool Polygons::insideOld(Point p, bool border_result) const
{
const Polygons& thiss = *this;
@@ -211,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;
+67 -23
Ver Arquivo
@@ -11,6 +11,8 @@
#include <limits> // int64_t.min
#include <list>
#include <initializer_list>
#include "intpoint.h"
#define CHECK_POLY_ACCESS
@@ -52,7 +54,7 @@ public:
Point& operator[] (unsigned int index) const
{
POLY_ASSERT(index < size() && index >= 0);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
return (*path)[index];
}
@@ -85,7 +87,7 @@ public:
void remove(unsigned int index)
{
POLY_ASSERT(index < size() && index >= 0);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
path->erase(path->begin() + index);
}
@@ -407,7 +409,7 @@ public:
PolygonRef operator[] (unsigned int index)
{
POLY_ASSERT(index < size() && index >= 0);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
return PolygonRef(paths[index]);
}
const PolygonRef operator[] (unsigned int index) const
@@ -430,11 +432,23 @@ public:
{
return paths.end();
}
/*!
* Remove a polygon from the list and move the last polygon to its place
*
* \warning changes the order of the polygons!
*/
void remove(unsigned int index)
{
POLY_ASSERT(index < size() && index >= 0);
paths.erase(paths.begin() + index);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
if (index < paths.size() - 1)
{
paths[index] = std::move(paths.back());
}
paths.resize(paths.size() - 1);
}
/*!
* Remove a range of polygons
*/
void erase(ClipperLib::Paths::iterator start, ClipperLib::Paths::iterator end)
{
paths.erase(start, end);
@@ -456,6 +470,13 @@ public:
for(unsigned int n=0; n<other.paths.size(); n++)
paths.push_back(other.paths[n]);
}
/*!
* Add a 'polygon' consisting of two points
*/
void addLine(const Point from, const Point to)
{
paths.emplace_back((std::initializer_list<Point>){from, to});
}
template<typename... Args>
void emplace_back(Args... args)
@@ -476,9 +497,6 @@ public:
Polygons() {}
Polygons(const Polygons& other) { paths = other.paths; }
virtual ~Polygons() {}
Polygons& operator=(const Polygons& other) { paths = other.paths; return *this; }
bool operator==(const Polygons& other) const =delete;
@@ -517,6 +535,20 @@ public:
clipper.Execute(ClipperLib::ctIntersection, ret.paths);
return ret;
}
/*!
* Clips input line segments by this Polygons.
* \param other Input line segments to be cropped
* \return the resulting interior line segments
*/
ClipperLib::PolyTree lineSegmentIntersection(const Polygons& other) const
{
ClipperLib::PolyTree ret;
ClipperLib::Clipper clipper(clipper_init);
clipper.AddPaths(paths, ClipperLib::ptClip, true);
clipper.AddPaths(other.paths, ClipperLib::ptSubject, false);
clipper.Execute(ClipperLib::ctIntersection, ret);
return ret;
}
Polygons xorPolygons(const Polygons& other) const
{
Polygons ret;
@@ -552,7 +584,7 @@ public:
* \param border_result What to return when the point is exactly on the border
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
*/
virtual bool inside(Point p, bool border_result = false) const;
bool inside(Point p, bool border_result = false) const;
/*!
* Check if we are inside the polygon. We do this by tracing from the point towards the positive X direction,
@@ -827,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;
@@ -896,20 +940,20 @@ public:
Polygons& thiss = *this;
return thiss[0];
}
/*!
* Check if we are inside the polygon.
*
* We do this by counting the number of polygons inside which this point lies.
* An odd number is inside, while an even number is outside.
*
* Returns false if outside, true if inside; if the point lies exactly on the border, will return \p border_result.
*
* \param p The point for which to check if it is inside this polygon
* \param border_result What to return when the point is exactly on the border
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
*/
virtual bool inside(Point p, bool border_result = false) const;
bool inside(Point p)
{
if (size() < 1)
return false;
if (!(*this)[0].inside(p))
return false;
for(unsigned int n=1; n<paths.size(); n++)
{
if ((*this)[n].inside(p))
return false;
}
return true;
}
};
/*!
+19 -6
Ver Arquivo
@@ -596,12 +596,25 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygo
{
ClosestPolygonPoint none;
if (polygons.size() == 0) return none;
PolygonRef aPolygon = polygons[0];
if (aPolygon.size() == 0) return none;
Point aPoint = aPolygon[0];
ClosestPolygonPoint best(aPoint, 0, aPolygon, 0);
if (polygons.size() == 0)
{
return none;
}
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
if (polygons[any_poly_idx].size() > 0)
{
any_polygon = polygons[any_poly_idx];
break;
}
}
if (any_polygon.size() == 0)
{
return none;
}
ClosestPolygonPoint best(any_polygon[0], 0, any_polygon, any_poly_idx);
int64_t closestDist2_score = vSize2(from - best.location) + penalty_function(best.location);
+9
Ver Arquivo
@@ -17,6 +17,15 @@ 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(Point& from, Point& to)
{
+10 -1
Ver Arquivo
@@ -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;
@@ -62,6 +64,13 @@ public:
*/
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.
* \param polygons The wall polygons for which to compute the overlaps
+3 -3
Ver Arquivo
@@ -36,9 +36,9 @@ void GCodePlannerTest::setUp()
fan_speed_layer_time_settings.cool_min_speed = 0.5;
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder;
fan_speed_layer_time_settings_per_extruder.push_back(fan_speed_layer_time_settings);
// Slice layer z layer last current fan speed and layer combing comb travel travel avoid
// storage nr height position extruder time settings mode offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings_per_extruder, CombingMode::OFF, 100, false, 50 );
// Slice layer z layer last current is inside fan speed and layer combing comb travel travel avoid
// storage nr height position extruder mesh time settings mode offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, false, fan_speed_layer_time_settings_per_extruder, CombingMode::OFF, 100, false, 50 );
}
void GCodePlannerTest::tearDown()