Imported Upstream version 1.31.6
Chow Loong Jin
7 years ago
81 | 81 | use constant VIEW_FRONT => [0.0,90.0]; |
82 | 82 | use constant VIEW_REAR => [180.0,90.0]; |
83 | 83 | |
84 | #use constant GIMBALL_LOCK_THETA_MAX => 150; | |
85 | use constant GIMBALL_LOCK_THETA_MAX => 170; | |
86 | ||
84 | 87 | # make OpenGL::Array thread-safe |
85 | 88 | { |
86 | 89 | no warnings 'redefine'; |
253 | 256 | if (TURNTABLE_MODE) { |
254 | 257 | $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); |
255 | 258 | $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- |
256 | $self->_stheta(150) if $self->_stheta > 150; | |
259 | $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; | |
257 | 260 | $self->_stheta(0) if $self->_stheta < 0; |
258 | 261 | } else { |
259 | 262 | my $size = $self->GetClientSize; |
358 | 361 | $self->_sphi($dirvec->[0]); |
359 | 362 | $self->_stheta($dirvec->[1]); |
360 | 363 | # Avoid gimball lock. |
361 | $self->_stheta(150) if $self->_stheta > 150; | |
364 | $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; | |
362 | 365 | $self->_stheta(0) if $self->_stheta < 0; |
363 | 366 | # View everything. |
364 | 367 | $self->zoom_to_bounding_box($bb); |
21 | 21 | |
22 | 22 | my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); |
23 | 23 | if ($^O eq 'MSWin32') { |
24 | $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r.ico"), wxBITMAP_TYPE_ICO)); | |
24 | # Load the icon either from the exe, or fron the ico file. | |
25 | my $iconfile = $Slic3r::var->('..\slic3r.exe'); | |
26 | $iconfile = $Slic3r::var->("Slic3r.ico") unless -f $iconfile; | |
27 | $self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO)); | |
25 | 28 | } else { |
26 | 29 | $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); |
27 | 30 | } |
29 | 29 | keep_upper => 1, |
30 | 30 | keep_lower => 1, |
31 | 31 | rotate_lower => 1, |
32 | preview => 1, | |
32 | # preview => 1, | |
33 | # Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. | |
34 | preview => 0, | |
33 | 35 | }; |
34 | 36 | |
35 | 37 | my $optgroup; |
257 | 259 | $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1); |
258 | 260 | $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1); |
259 | 261 | $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower}); |
260 | $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); | |
262 | # Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. | |
263 | # $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); | |
261 | 264 | |
262 | 265 | # update cut button |
263 | 266 | if (($self->{cut_options}{keep_upper} && $have_upper) |
818 | 818 | $new_conf->set("spiral_vase", 0); |
819 | 819 | $self->load_config($new_conf); |
820 | 820 | } |
821 | } | |
822 | ||
823 | if ($config->support_material) { | |
824 | # Ask only once. | |
825 | if (! $self->{support_material_overhangs_queried}) { | |
826 | $self->{support_material_overhangs_queried} = 1; | |
827 | if ($config->overhangs != 1) { | |
828 | my $dialog = Wx::MessageDialog->new($self, | |
829 | "Supports work better, if the following feature is enabled:\n" | |
830 | . "- Detect bridging perimeters\n" | |
831 | . "\nShall I adjust those settings for supports?", | |
832 | 'Support Generator', wxICON_WARNING | wxYES | wxNO | wxCANCEL); | |
833 | my $answer = $dialog->ShowModal(); | |
834 | my $new_conf = Slic3r::Config->new; | |
835 | if ($answer == wxID_YES) { | |
836 | # Enable "detect bridging perimeters". | |
837 | $new_conf->set("overhangs", 1); | |
838 | } elsif ($answer == wxID_NO) { | |
839 | # Do nothing, leave supports on and "detect bridging perimeters" off. | |
840 | } elsif ($answer == wxID_CANCEL) { | |
841 | # Disable supports. | |
842 | $new_conf->set("support_material", 0); | |
843 | $self->{support_material_overhangs_queried} = 0; | |
844 | } | |
845 | $self->load_config($new_conf); | |
846 | } | |
847 | } | |
848 | } else { | |
849 | $self->{support_material_overhangs_queried} = 0; | |
821 | 850 | } |
822 | 851 | |
823 | 852 | if ($config->fill_density == 100 |
258 | 258 | } |
259 | 259 | } |
260 | 260 | |
261 | # merge all regions' slices to get islands | |
261 | # Merge all regions' slices to get islands, chain them by a shortest path. | |
262 | 262 | $layer->make_slices; |
263 | 263 | } |
264 | 264 | |
366 | 366 | return $mesh->slice($z); |
367 | 367 | } |
368 | 368 | |
369 | # 1) Merges typed region slices into stInternal type. | |
370 | # 2) Increases an "extra perimeters" counter at region slices where needed. | |
371 | # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). | |
369 | 372 | sub make_perimeters { |
370 | 373 | my $self = shift; |
371 | 374 | |
376 | 379 | $self->set_step_started(STEP_PERIMETERS); |
377 | 380 | $self->print->status_cb->(20, "Generating perimeters"); |
378 | 381 | |
379 | # merge slices if they were split into types | |
382 | # Merge region slices if they were split into types. | |
383 | # FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration. | |
380 | 384 | if ($self->typed_slices) { |
381 | 385 | $_->merge_slices for @{$self->layers}; |
382 | 386 | $self->set_typed_slices(0); |
486 | 490 | $self->set_step_started(STEP_PREPARE_INFILL); |
487 | 491 | $self->print->status_cb->(30, "Preparing infill"); |
488 | 492 | |
489 | # this will assign a type (top/bottom/internal) to $layerm->slices | |
490 | # and transform $layerm->fill_surfaces from expolygon | |
491 | # to typed top/bottom/internal surfaces; | |
493 | # This will assign a type (top/bottom/internal) to $layerm->slices. | |
494 | # Then the classifcation of $layerm->slices is transfered onto | |
495 | # the $layerm->fill_surfaces by clipping $layerm->fill_surfaces | |
496 | # by the cummulative area of the previous $layerm->fill_surfaces. | |
492 | 497 | $self->detect_surfaces_type; |
493 | # Mark the object to have the slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) | |
498 | # Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) | |
494 | 499 | $self->set_typed_slices(1); |
495 | 500 | |
496 | 501 | # Decide what surfaces are to be filled. |
614 | 619 | ); |
615 | 620 | |
616 | 621 | ### we could free memory now, but this would make this step not idempotent |
622 | ### Vojtech: Cannot release the fill_surfaces, they are used by the support generator. | |
617 | 623 | ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; |
618 | 624 | |
619 | 625 | $self->set_step_done(STEP_INFILL); |
33 | 33 | expolygon => $_, |
34 | 34 | )) for @$expolygons; |
35 | 35 | |
36 | my ($region_config, $object_config, $print_config, $loops, $gap_fill, $perimeter_surfaces, $fill_surfaces); | |
36 | my ($region_config, $object_config, $print_config, $loops, $gap_fill, $fill_surfaces); | |
37 | 37 | my $g = Slic3r::Layer::PerimeterGenerator->new( |
38 | 38 | # input: |
39 | 39 | $slices, |
46 | 46 | # output: |
47 | 47 | ($loops = Slic3r::ExtrusionPath::Collection->new), |
48 | 48 | ($gap_fill = Slic3r::ExtrusionPath::Collection->new), |
49 | ($perimeter_surfaces = Slic3r::Surface::Collection->new), | |
50 | 49 | ($fill_surfaces = Slic3r::Surface::Collection->new), |
51 | 50 | ); |
52 | 51 | $g->config->apply_dynamic($config); |
Binary diff not shown
22 | 22 | # In case windows.h is included, we don't want the min / max macros to be active. |
23 | 23 | # If <math.h> is included, we want the #defines to be active (M_PI etc.) |
24 | 24 | push @cflags, qw(-D_WIN32 -DNOMINMAX -D_USE_MATH_DEFINES); |
25 | } | |
26 | if (! $cpp_guess->is_msvc) { | |
27 | # Don't use the version flag on MS Visual Studio, as it starts to recognize them up to 2015 and it uses different syntax. | |
28 | push @cflags, qw(-std=c++11); | |
25 | 29 | } |
26 | 30 | |
27 | 31 | my @early_includes = (); |
39 | 43 | $alienwx_libraries =~ s/-L/-LIBPATH:/g if ($cpp_guess->is_msvc); |
40 | 44 | push @ldflags, Alien::wxWidgets->link_flags, $alienwx_libraries; |
41 | 45 | # push @early_includes, qw(slic3r/GUI/wxinit.h); |
46 | } | |
47 | ||
48 | if ($ENV{SLIC3R_PROFILE}) | |
49 | { | |
50 | print "Slic3r will be built with a Shiny invasive profiler\n"; | |
51 | push @cflags, qw(-DSLIC3R_PROFILE); | |
42 | 52 | } |
43 | 53 | |
44 | 54 | if ($ENV{SLIC3R_HAS_BROKEN_CROAK}) |
27 | 27 | /*---------------------------------------------------------------------------*/ |
28 | 28 | |
29 | 29 | #include "ShinyMacros.h" |
30 | ||
31 | #ifdef SLIC3R_PROFILE | |
30 | 32 | #include "ShinyManager.h" |
33 | #endif /* SLIC3R_PROFILE */ | |
31 | 34 | |
32 | 35 | #endif /* SHINY_H */ |
26 | 26 | |
27 | 27 | |
28 | 28 | /*---------------------------------------------------------------------------*/ |
29 | ||
30 | /* SHINY_IS_COMPILED is the master on or off swith at compile time. Define it to TRUE or FALSE before including header Shiny.h or inside ShinyConfig.h. Default is TRUE. | |
31 | */ | |
32 | #if defined(SLIC3R_PROFILE) && defined(WIN32) | |
33 | #define SHINY_IS_COMPILED TRUE | |
34 | #else | |
35 | #define SHINY_IS_COMPILED FALSE | |
36 | #endif | |
37 | ||
38 | #define SHINY_STATIC_LINK TRUE | |
39 | 29 | |
40 | 30 | /* if SHINY_LOOKUP_RATE is defined to TRUE then Shiny will record the success of its hash function. This is useful for debugging. Default is FALSE. |
41 | 31 | */ |
24 | 24 | #ifndef SHINY_MACROS_H |
25 | 25 | #define SHINY_MACROS_H |
26 | 26 | |
27 | #ifdef SLIC3R_PROFILE | |
28 | ||
27 | 29 | #include "ShinyManager.h" |
28 | ||
29 | #if SHINY_IS_COMPILED == TRUE | |
30 | 30 | |
31 | 31 | /*---------------------------------------------------------------------------*/ |
32 | 32 | /* public preprocessors */ |
253 | 253 | |
254 | 254 | /*---------------------------------------------------------------------------*/ |
255 | 255 | |
256 | #else /* if SHINY_IS_COMPILED == TRUE */ | |
257 | ||
258 | #ifdef __cplusplus | |
259 | extern "C" { | |
260 | #endif | |
261 | SHINY_INLINE ShinyData GetEmptyData() { | |
262 | ShinyData a = { { 0, 0 }, { 0, 0 }, { 0, 0 } }; | |
263 | return a; | |
264 | } | |
265 | #ifdef __cplusplus | |
266 | } /* end of extern "C" */ | |
267 | #endif | |
256 | #else /* SLIC3R_PROFILE */ | |
268 | 257 | |
269 | 258 | #define PROFILE_UPDATE() |
270 | 259 | #define PROFILE_SET_DAMPING(x) |
284 | 273 | #define PROFILE_SHARED_DEFINE(name) |
285 | 274 | #define PROFILE_SHARED_BEGIN(name) |
286 | 275 | #define PROFILE_SHARED_BLOCK(name) |
287 | #define PROFILE_GET_SHARED_DATA(name) ShinyGetEmptyData() | |
288 | #define PROFILE_GET_ROOT_DATA() ShinyGetEmptyData() | |
289 | ||
290 | #if SHINY_HAS_ENABLED == TRUE | |
291 | 276 | #define PROFILE_SET_ENABLED(boolean) |
292 | #endif | |
293 | ||
294 | #endif /* SHINY_IS_COMPILED == TRUE */ | |
277 | ||
278 | #endif /* SLIC3R_PROFILE */ | |
295 | 279 | |
296 | 280 | #endif /* SHINY_MACROS_H */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyManager.h" |
25 | 27 | |
26 | // #include <malloc.h> | |
28 | #include <malloc.h> | |
27 | 29 | #include <memory.h> |
28 | 30 | #include <string.h> |
29 | 31 | #include <stdio.h> |
30 | ||
31 | #if SHINY_IS_COMPILED == TRUE | |
32 | ||
33 | 32 | |
34 | 33 | /*---------------------------------------------------------------------------*/ |
35 | 34 | |
442 | 441 | #endif |
443 | 442 | } |
444 | 443 | |
445 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
444 | #endif /* SLIC3R_PROFILE */ |
32 | 32 | |
33 | 33 | #include <stdio.h> |
34 | 34 | |
35 | #if SHINY_IS_COMPILED == TRUE | |
36 | ||
37 | 35 | #ifdef __cplusplus |
38 | 36 | extern "C" { |
39 | 37 | #endif |
261 | 259 | }; |
262 | 260 | #endif |
263 | 261 | |
264 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
265 | ||
266 | 262 | #endif /* SHINY_MANAGER_H */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyNode.h" |
25 | 27 | #include "ShinyZone.h" |
26 | 28 | #include "ShinyNodeState.h" |
27 | 29 | |
28 | 30 | #include <memory.h> |
29 | ||
30 | ||
31 | #if SHINY_IS_COMPILED == TRUE | |
32 | 31 | |
33 | 32 | /*---------------------------------------------------------------------------*/ |
34 | 33 | |
126 | 125 | if (a_node->nextSibling) ShinyNode_enumerateNodes(a_node->nextSibling, a_func); |
127 | 126 | } |
128 | 127 | |
129 | #endif | |
128 | #endif /* SLIC3R_PROFILE */ |
26 | 26 | |
27 | 27 | #include "ShinyData.h" |
28 | 28 | #include "ShinyTools.h" |
29 | ||
30 | #if SHINY_IS_COMPILED == TRUE | |
31 | 29 | |
32 | 30 | #ifdef __cplusplus |
33 | 31 | extern "C" { |
131 | 129 | } |
132 | 130 | #endif /* __cplusplus */ |
133 | 131 | |
134 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
135 | ||
136 | 132 | #endif /* SHINY_NODE_H */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyNodePool.h" |
25 | 27 | #include "ShinyTools.h" |
26 | 28 | |
27 | 29 | #include <memory.h> |
28 | // #include <malloc.h> | |
29 | ||
30 | #if SHINY_IS_COMPILED == TRUE | |
31 | ||
30 | #include <malloc.h> | |
32 | 31 | |
33 | 32 | /*---------------------------------------------------------------------------*/ |
34 | 33 | |
74 | 73 | free(self); |
75 | 74 | } |
76 | 75 | |
77 | #endif | |
76 | #endif /* SLIC3R_PROFILE */ |
25 | 25 | #define SHINY_NODE_POOL_H |
26 | 26 | |
27 | 27 | #include "ShinyNode.h" |
28 | ||
29 | #if SHINY_IS_COMPILED == TRUE | |
30 | 28 | |
31 | 29 | #ifdef __cplusplus |
32 | 30 | extern "C" { |
65 | 63 | } /* end of extern "C" */ |
66 | 64 | #endif |
67 | 65 | |
68 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
69 | ||
70 | ||
71 | 66 | #endif /* SHINY_NODE_POOL_H */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyNodeState.h" |
25 | 27 | #include "ShinyNode.h" |
26 | 28 | #include "ShinyZone.h" |
27 | 29 | |
28 | // #include <malloc.h> | |
29 | ||
30 | ||
31 | #if SHINY_IS_COMPILED == TRUE | |
30 | #include <malloc.h> | |
32 | 31 | |
33 | 32 | /*---------------------------------------------------------------------------*/ |
34 | 33 | |
105 | 104 | return node->nextSibling; |
106 | 105 | } |
107 | 106 | |
108 | #endif | |
107 | #endif /* SLIC3R_PROFILE */ |
26 | 26 | |
27 | 27 | #include "ShinyNode.h" |
28 | 28 | |
29 | #if SHINY_IS_COMPILED == TRUE | |
30 | ||
31 | 29 | #ifdef __cplusplus |
32 | 30 | extern "C" { |
33 | 31 | #endif |
54 | 52 | } /* end of extern "C" */ |
55 | 53 | #endif |
56 | 54 | |
57 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
58 | ||
59 | 55 | #endif /* SHINY_NODE_STATE_H */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyOutput.h" |
25 | 27 | |
26 | 28 | #include <stdio.h> |
34 | 36 | # define TRAILING 1 |
35 | 37 | #endif |
36 | 38 | |
37 | #if SHINY_IS_COMPILED == TRUE | |
38 | ||
39 | ||
40 | 39 | /*---------------------------------------------------------------------------*/ |
41 | 40 | |
42 | 41 | #define OUTPUT_WIDTH_CALL 6 |
43 | 42 | #define OUTPUT_WIDTH_TIME 6 |
44 | 43 | #define OUTPUT_WIDTH_PERC 4 |
45 | #define OUTPUT_WIDTH_SUM 79 | |
44 | #define OUTPUT_WIDTH_SUM 120 | |
46 | 45 | |
47 | 46 | #define OUTPUT_WIDTH_DATA (1+OUTPUT_WIDTH_CALL + 1 + 2*(OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1) + 1) |
48 | 47 | #define OUTPUT_WIDTH_NAME (OUTPUT_WIDTH_SUM - OUTPUT_WIDTH_DATA) |
186 | 185 | } |
187 | 186 | } |
188 | 187 | |
189 | #endif | |
188 | #endif /* SLIC3R_PROFILE */ |
26 | 26 | |
27 | 27 | #include "ShinyNode.h" |
28 | 28 | #include "ShinyZone.h" |
29 | ||
30 | #if SHINY_IS_COMPILED == TRUE | |
31 | 29 | |
32 | 30 | #ifdef __cplusplus |
33 | 31 | extern "C" { |
66 | 64 | } |
67 | 65 | #endif /* __cplusplus */ |
68 | 66 | |
69 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
70 | ||
71 | 67 | #endif /* SHINY_OUTPUT_H */ |
85 | 85 | |
86 | 86 | /*---------------------------------------------------------------------------*/ |
87 | 87 | |
88 | #if SHINY_IS_COMPILED == TRUE | |
89 | struct _ShinyNode; | |
90 | struct _ShinyZone; | |
88 | struct _ShinyNode; | |
89 | struct _ShinyZone; | |
91 | 90 | |
92 | typedef struct _ShinyNode* ShinyNodeCache; | |
93 | typedef struct _ShinyNode* ShinyNodeTable; | |
94 | #endif | |
95 | ||
91 | typedef struct _ShinyNode* ShinyNodeCache; | |
92 | typedef struct _ShinyNode* ShinyNodeTable; | |
96 | 93 | |
97 | 94 | /*---------------------------------------------------------------------------*/ |
98 | 95 | |
99 | #ifdef SHINY_STATIC_LINK | |
100 | # define SHINY_API | |
101 | #else | |
102 | # define SHINY_API SHINY_EXPORT | |
103 | #endif | |
104 | ||
96 | #define SHINY_API | |
105 | 97 | |
106 | 98 | /*---------------------------------------------------------------------------*/ |
107 | 99 | |
108 | 100 | #if SHINY_COMPILER == SHINY_COMPILER_MSVC |
109 | 101 | # define SHINY_INLINE __inline |
110 | 102 | # define SHINY_UNUSED |
111 | # define SHINY_EXPORT __declspec(dllexport) | |
112 | ||
113 | 103 | #elif SHINY_COMPILER == SHINY_COMPILER_GNUC |
114 | 104 | # define SHINY_INLINE inline |
115 | 105 | # define SHINY_UNUSED __attribute__((unused)) |
116 | # define SHINY_EXPORT __attribute__((dllexport)) | |
117 | ||
118 | 106 | #elif SHINY_COMPILER == SHINY_COMPILER_OTHER |
119 | 107 | # define SHINY_INLINE inline |
120 | 108 | # define SHINY_UNUSED |
121 | # define SHINY_EXPORT extern | |
122 | 109 | #endif |
123 | 110 | |
124 | 111 |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #if SHINY_IS_COMPILED == TRUE | |
24 | #ifdef SLIC3R_PROFILE | |
25 | 25 | |
26 | 26 | #include "ShinyTools.h" |
27 | 27 | |
109 | 109 | |
110 | 110 | #endif |
111 | 111 | |
112 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
112 | #endif /* SLIC3R_PROFILE */ |
21 | 21 | THE SOFTWARE. |
22 | 22 | */ |
23 | 23 | |
24 | #ifdef SLIC3R_PROFILE | |
25 | ||
24 | 26 | #include "ShinyZone.h" |
25 | 27 | |
26 | 28 | #include <memory.h> |
27 | ||
28 | #if SHINY_IS_COMPILED == TRUE | |
29 | 29 | |
30 | 30 | /*---------------------------------------------------------------------------*/ |
31 | 31 | |
197 | 197 | if (a_zone->next) ShinyZone_enumerateZones(a_zone->next, a_func); |
198 | 198 | } |
199 | 199 | |
200 | #endif | |
200 | #endif /* SLIC3R_PROFILE */ |
26 | 26 | |
27 | 27 | #include "ShinyData.h" |
28 | 28 | #include <memory.h> |
29 | ||
30 | #if SHINY_IS_COMPILED == TRUE | |
31 | 29 | |
32 | 30 | #ifdef __cplusplus |
33 | 31 | extern "C" { |
89 | 87 | } |
90 | 88 | #endif /* __cplusplus */ |
91 | 89 | |
92 | #endif /* if SHINY_IS_COMPILED == TRUE */ | |
93 | ||
94 | 90 | #endif /* SHINY_ZONE_H */ |
105 | 105 | } |
106 | 106 | } |
107 | 107 | stl_free_edges(stl); |
108 | ||
109 | #if 0 | |
110 | printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", | |
111 | stl->stats.number_of_facets, stl->stats.number_of_facets * 3, | |
112 | stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); | |
113 | #endif | |
108 | 114 | } |
109 | 115 | |
110 | 116 | static void |
111 | 117 | stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, |
112 | 118 | stl_vertex *a, stl_vertex *b) { |
113 | 119 | |
114 | float diff_x; | |
115 | float diff_y; | |
116 | float diff_z; | |
117 | float max_diff; | |
118 | ||
119 | if (stl->error) return; | |
120 | ||
121 | diff_x = ABS(a->x - b->x); | |
122 | diff_y = ABS(a->y - b->y); | |
123 | diff_z = ABS(a->z - b->z); | |
124 | max_diff = STL_MAX(diff_x, diff_y); | |
125 | max_diff = STL_MAX(diff_z, max_diff); | |
126 | stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); | |
127 | ||
128 | if(diff_x == max_diff) { | |
129 | if(a->x > b->x) { | |
130 | memcpy(&edge->key[0], a, sizeof(stl_vertex)); | |
131 | memcpy(&edge->key[3], b, sizeof(stl_vertex)); | |
132 | } else { | |
133 | memcpy(&edge->key[0], b, sizeof(stl_vertex)); | |
134 | memcpy(&edge->key[3], a, sizeof(stl_vertex)); | |
135 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
136 | } | |
137 | } else if(diff_y == max_diff) { | |
138 | if(a->y > b->y) { | |
139 | memcpy(&edge->key[0], a, sizeof(stl_vertex)); | |
140 | memcpy(&edge->key[3], b, sizeof(stl_vertex)); | |
141 | } else { | |
142 | memcpy(&edge->key[0], b, sizeof(stl_vertex)); | |
143 | memcpy(&edge->key[3], a, sizeof(stl_vertex)); | |
144 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
145 | } | |
146 | } else { | |
147 | if(a->z > b->z) { | |
148 | memcpy(&edge->key[0], a, sizeof(stl_vertex)); | |
149 | memcpy(&edge->key[3], b, sizeof(stl_vertex)); | |
150 | } else { | |
151 | memcpy(&edge->key[0], b, sizeof(stl_vertex)); | |
152 | memcpy(&edge->key[3], a, sizeof(stl_vertex)); | |
153 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
154 | } | |
120 | if (stl->error) return; | |
121 | ||
122 | { | |
123 | float diff_x = ABS(a->x - b->x); | |
124 | float diff_y = ABS(a->y - b->y); | |
125 | float diff_z = ABS(a->z - b->z); | |
126 | float max_diff = STL_MAX(diff_x, diff_y); | |
127 | max_diff = STL_MAX(diff_z, max_diff); | |
128 | stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); | |
129 | } | |
130 | ||
131 | // Ensure identical vertex ordering of equal edges. | |
132 | // This method is numerically robust. | |
133 | if ((a->x != b->x) ? | |
134 | (a->x < b->x) : | |
135 | ((a->y != b->y) ? | |
136 | (a->y < b->y) : | |
137 | (a->z < b->z))) { | |
138 | memcpy(&edge->key[0], a, sizeof(stl_vertex)); | |
139 | memcpy(&edge->key[3], b, sizeof(stl_vertex)); | |
140 | } else { | |
141 | memcpy(&edge->key[0], b, sizeof(stl_vertex)); | |
142 | memcpy(&edge->key[3], a, sizeof(stl_vertex)); | |
143 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
155 | 144 | } |
156 | 145 | } |
157 | 146 | |
308 | 297 | static int |
309 | 298 | stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, |
310 | 299 | stl_vertex *a, stl_vertex *b, float tolerance) { |
311 | float diff_x; | |
312 | float diff_y; | |
313 | float diff_z; | |
314 | float max_diff; | |
315 | unsigned vertex1[3]; | |
316 | unsigned vertex2[3]; | |
317 | ||
318 | ||
319 | diff_x = ABS(a->x - b->x); | |
320 | diff_y = ABS(a->y - b->y); | |
321 | diff_z = ABS(a->z - b->z); | |
322 | max_diff = STL_MAX(diff_x, diff_y); | |
323 | max_diff = STL_MAX(diff_z, max_diff); | |
324 | ||
325 | vertex1[0] = (unsigned)((a->x - stl->stats.min.x) / tolerance); | |
326 | vertex1[1] = (unsigned)((a->y - stl->stats.min.y) / tolerance); | |
327 | vertex1[2] = (unsigned)((a->z - stl->stats.min.z) / tolerance); | |
328 | vertex2[0] = (unsigned)((b->x - stl->stats.min.x) / tolerance); | |
329 | vertex2[1] = (unsigned)((b->y - stl->stats.min.y) / tolerance); | |
330 | vertex2[2] = (unsigned)((b->z - stl->stats.min.z) / tolerance); | |
300 | // Index of a grid cell spaced by tolerance. | |
301 | uint32_t vertex1[3] = { | |
302 | (uint32_t)((a->x - stl->stats.min.x) / tolerance), | |
303 | (uint32_t)((a->y - stl->stats.min.y) / tolerance), | |
304 | (uint32_t)((a->z - stl->stats.min.z) / tolerance) | |
305 | }; | |
306 | uint32_t vertex2[3] = { | |
307 | (uint32_t)((b->x - stl->stats.min.x) / tolerance), | |
308 | (uint32_t)((b->y - stl->stats.min.y) / tolerance), | |
309 | (uint32_t)((b->z - stl->stats.min.z) / tolerance) | |
310 | }; | |
331 | 311 | |
332 | 312 | if( (vertex1[0] == vertex2[0]) |
333 | 313 | && (vertex1[1] == vertex2[1]) |
336 | 316 | return 0; |
337 | 317 | } |
338 | 318 | |
339 | if(diff_x == max_diff) { | |
340 | if(a->x > b->x) { | |
341 | memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); | |
342 | memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); | |
343 | } else { | |
344 | memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); | |
345 | memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); | |
346 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
347 | } | |
348 | } else if(diff_y == max_diff) { | |
349 | if(a->y > b->y) { | |
350 | memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); | |
351 | memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); | |
352 | } else { | |
353 | memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); | |
354 | memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); | |
355 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
356 | } | |
357 | } else { | |
358 | if(a->z > b->z) { | |
359 | memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); | |
360 | memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); | |
361 | } else { | |
362 | memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); | |
363 | memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); | |
364 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
365 | } | |
319 | // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. | |
320 | // This method is numerically robust. | |
321 | if ((vertex1[0] != vertex2[0]) ? | |
322 | (vertex1[0] < vertex2[0]) : | |
323 | ((vertex1[1] != vertex2[1]) ? | |
324 | (vertex1[1] < vertex2[1]) : | |
325 | (vertex1[2] < vertex2[2]))) { | |
326 | memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); | |
327 | memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); | |
328 | } else { | |
329 | memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); | |
330 | memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); | |
331 | edge->which_edge += 3; /* this edge is loaded backwards */ | |
366 | 332 | } |
367 | 333 | return 1; |
368 | 334 | } |
563 | 529 | next_edge = pivot_vertex; |
564 | 530 | } |
565 | 531 | } |
532 | #if 0 | |
533 | if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && | |
534 | stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && | |
535 | stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) | |
536 | printf("Changing vertex %f,%f,%f: Same !!!\r\n", | |
537 | new_vertex.x, new_vertex.y, new_vertex.z); | |
538 | else { | |
539 | if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) | |
540 | printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", | |
541 | stl->facet_start[facet_num].vertex[pivot_vertex].x, | |
542 | *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].x), | |
543 | new_vertex.x, | |
544 | *reinterpret_cast<const int*>(&new_vertex.x)); | |
545 | if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) | |
546 | printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", | |
547 | stl->facet_start[facet_num].vertex[pivot_vertex].y, | |
548 | *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].y), | |
549 | new_vertex.y, | |
550 | *reinterpret_cast<const int*>(&new_vertex.y)); | |
551 | if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) | |
552 | printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", | |
553 | stl->facet_start[facet_num].vertex[pivot_vertex].z, | |
554 | *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].z), | |
555 | new_vertex.z, | |
556 | *reinterpret_cast<const int*>(&new_vertex.z)); | |
557 | } | |
558 | #endif | |
566 | 559 | stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; |
567 | 560 | vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; |
568 | 561 | facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; |
23 | 23 | #define __admesh_stl__ |
24 | 24 | |
25 | 25 | #include <stdio.h> |
26 | #include <stdint.h> | |
27 | #include <stddef.h> | |
28 | #include <boost/predef/detail/endian_compat.h> | |
29 | ||
30 | #ifndef BOOST_LITTLE_ENDIAN | |
31 | #error "admesh works correctly on little endian machines only!" | |
32 | #endif | |
26 | 33 | |
27 | 34 | #ifdef __cplusplus |
28 | 35 | extern "C" { |
32 | 39 | #define STL_MIN(A,B) ((A)<(B)? (A):(B)) |
33 | 40 | #define ABS(X) ((X) < 0 ? -(X) : (X)) |
34 | 41 | |
42 | // Size of the binary STL header, free form. | |
35 | 43 | #define LABEL_SIZE 80 |
44 | // Binary STL, length of the "number of faces" counter. | |
36 | 45 | #define NUM_FACET_SIZE 4 |
46 | // Binary STL, sizeof header + number of faces. | |
37 | 47 | #define HEADER_SIZE 84 |
38 | 48 | #define STL_MIN_FILE_SIZE 284 |
39 | 49 | #define ASCII_LINES_PER_FACET 7 |
50 | // Comparing an edge by memcmp, 2x3x4 bytes = 24 | |
40 | 51 | #define SIZEOF_EDGE_SORT 24 |
41 | 52 | |
42 | 53 | typedef struct { |
45 | 56 | float z; |
46 | 57 | } stl_vertex; |
47 | 58 | |
59 | static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); | |
60 | ||
48 | 61 | typedef struct { |
49 | 62 | float x; |
50 | 63 | float y; |
51 | 64 | float z; |
52 | 65 | } stl_normal; |
66 | ||
67 | static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); | |
53 | 68 | |
54 | 69 | typedef char stl_extra[2]; |
55 | 70 | |
60 | 75 | } stl_facet; |
61 | 76 | #define SIZEOF_STL_FACET 50 |
62 | 77 | |
78 | static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset"); | |
79 | static_assert(offsetof(stl_facet, vertex) == 12, "stl_facet.vertex has correct offset"); | |
80 | static_assert(offsetof(stl_facet, extra ) == 48, "stl_facet.extra has correct offset"); | |
81 | static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrect"); | |
82 | ||
63 | 83 | typedef enum {binary, ascii, inmemory} stl_type; |
64 | 84 | |
65 | 85 | typedef struct { |
69 | 89 | } stl_edge; |
70 | 90 | |
71 | 91 | typedef struct stl_hash_edge { |
72 | unsigned key[6]; | |
92 | // Key of a hash edge: 2x binary copy of a floating point vertex. | |
93 | uint32_t key[6]; | |
94 | // Index of a facet owning this edge. | |
73 | 95 | int facet_number; |
96 | // Index of this edge inside the facet with an index of facet_number. | |
97 | // If this edge is stored backwards, which_edge is increased by 3. | |
74 | 98 | int which_edge; |
75 | 99 | struct stl_hash_edge *next; |
76 | 100 | } stl_hash_edge; |
77 | 101 | |
78 | typedef struct { | |
102 | static_assert(offsetof(stl_hash_edge, facet_number) == SIZEOF_EDGE_SORT, "size of stl_hash_edge.key incorrect"); | |
103 | ||
104 | typedef struct { | |
105 | // Index of a neighbor facet. | |
79 | 106 | int neighbor[3]; |
107 | // Index of an opposite vertex at the neighbor face. | |
80 | 108 | char which_vertex_not[3]; |
81 | 109 | } stl_neighbors; |
82 | 110 |
203 | 203 | } |
204 | 204 | |
205 | 205 | void |
206 | stl_put_little_int(FILE *fp, int value_in) { | |
207 | int new_value; | |
208 | union { | |
209 | int int_value; | |
210 | char char_value[4]; | |
211 | } value; | |
212 | ||
213 | value.int_value = value_in; | |
214 | ||
215 | new_value = value.char_value[0] & 0xFF; | |
216 | new_value |= (value.char_value[1] & 0xFF) << 0x08; | |
217 | new_value |= (value.char_value[2] & 0xFF) << 0x10; | |
218 | new_value |= (value.char_value[3] & 0xFF) << 0x18; | |
219 | fwrite(&new_value, sizeof(int), 1, fp); | |
220 | } | |
221 | ||
222 | void | |
223 | stl_put_little_float(FILE *fp, float value_in) { | |
224 | int new_value; | |
225 | union { | |
226 | float float_value; | |
227 | char char_value[4]; | |
228 | } value; | |
229 | ||
230 | value.float_value = value_in; | |
231 | ||
232 | new_value = value.char_value[0] & 0xFF; | |
233 | new_value |= (value.char_value[1] & 0xFF) << 0x08; | |
234 | new_value |= (value.char_value[2] & 0xFF) << 0x10; | |
235 | new_value |= (value.char_value[3] & 0xFF) << 0x18; | |
236 | fwrite(&new_value, sizeof(int), 1, fp); | |
237 | } | |
238 | ||
239 | void | |
240 | stl_write_binary_block(stl_file *stl, FILE *fp) | |
241 | { | |
242 | int i; | |
243 | for(i = 0; i < stl->stats.number_of_facets; i++) | |
244 | { | |
245 | stl_put_little_float(fp, stl->facet_start[i].normal.x); | |
246 | stl_put_little_float(fp, stl->facet_start[i].normal.y); | |
247 | stl_put_little_float(fp, stl->facet_start[i].normal.z); | |
248 | stl_put_little_float(fp, stl->facet_start[i].vertex[0].x); | |
249 | stl_put_little_float(fp, stl->facet_start[i].vertex[0].y); | |
250 | stl_put_little_float(fp, stl->facet_start[i].vertex[0].z); | |
251 | stl_put_little_float(fp, stl->facet_start[i].vertex[1].x); | |
252 | stl_put_little_float(fp, stl->facet_start[i].vertex[1].y); | |
253 | stl_put_little_float(fp, stl->facet_start[i].vertex[1].z); | |
254 | stl_put_little_float(fp, stl->facet_start[i].vertex[2].x); | |
255 | stl_put_little_float(fp, stl->facet_start[i].vertex[2].y); | |
256 | stl_put_little_float(fp, stl->facet_start[i].vertex[2].z); | |
257 | fputc(stl->facet_start[i].extra[0], fp); | |
258 | fputc(stl->facet_start[i].extra[1], fp); | |
259 | } | |
260 | } | |
261 | ||
262 | void | |
263 | 206 | stl_write_binary(stl_file *stl, const char *file, const char *label) { |
264 | 207 | FILE *fp; |
265 | 208 | int i; |
284 | 227 | for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); |
285 | 228 | |
286 | 229 | fseek(fp, LABEL_SIZE, SEEK_SET); |
287 | ||
288 | stl_put_little_int(fp, stl->stats.number_of_facets); | |
289 | ||
290 | stl_write_binary_block(stl, fp); | |
291 | ||
230 | fwrite(&stl->stats.number_of_facets, 4, 1, fp); | |
231 | for(i = 0; i < stl->stats.number_of_facets; i++) | |
232 | fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); | |
292 | 233 | fclose(fp); |
293 | 234 | } |
294 | 235 |
26 | 26 | |
27 | 27 | #include "stl.h" |
28 | 28 | |
29 | #if !defined(SEEK_SET) | |
30 | #define SEEK_SET 0 | |
31 | #define SEEK_CUR 1 | |
32 | #define SEEK_END 2 | |
29 | #ifndef SEEK_SET | |
30 | #error "SEEK_SET not defined" | |
33 | 31 | #endif |
34 | 32 | |
35 | 33 | void |
276 | 274 | /* Read a single facet from a binary .STL file */ |
277 | 275 | { |
278 | 276 | /* we assume little-endian architecture! */ |
279 | if (fread(&facet.normal, sizeof(stl_normal), 1, stl->fp) \ | |
280 | + fread(&facet.vertex, sizeof(stl_vertex), 3, stl->fp) \ | |
281 | + fread(&facet.extra, sizeof(char), 2, stl->fp) != 6) { | |
282 | perror("Cannot read facet"); | |
277 | if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) { | |
283 | 278 | stl->error = 1; |
284 | 279 | return; |
285 | 280 | } |
303 | 298 | return; |
304 | 299 | } |
305 | 300 | } |
301 | ||
302 | #if 0 | |
303 | // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, | |
304 | // close to zero values may be represented with singificantly higher precision than the rest of the vertices. | |
305 | // It may be worth to round these numbers to zero during loading to reduce the number of errors reported | |
306 | // during the STL import. | |
307 | for (size_t j = 0; j < 3; ++ j) { | |
308 | if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) | |
309 | printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); | |
310 | if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) | |
311 | printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); | |
312 | if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) | |
313 | printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); | |
314 | } | |
315 | #endif | |
316 | ||
317 | #if 1 | |
318 | { | |
319 | // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. | |
320 | // When using a memcmp on raw floats, those numbers report to be different. | |
321 | // Unify all +0 and -0 to +0 to make the floats equal under memcmp. | |
322 | uint32_t *f = (uint32_t*)&facet; | |
323 | for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats | |
324 | if (*f == 0x80000000) | |
325 | // Negative zero, switch to positive zero. | |
326 | *f = 0; | |
327 | } | |
328 | #else | |
329 | { | |
330 | // Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision | |
331 | // than the rest of the vertices. Round them to zero. | |
332 | float *f = (float*)&facet; | |
333 | for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats | |
334 | if (*f > -1e-12f && *f < 1e-12f) | |
335 | // Negative zero, switch to positive zero. | |
336 | *f = 0; | |
337 | } | |
338 | #endif | |
306 | 339 | /* Write the facet into memory. */ |
307 | stl->facet_start[i] = facet; | |
308 | ||
340 | memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); | |
309 | 341 | stl_facet_stats(stl, facet, first); |
310 | 342 | first = 0; |
311 | 343 | } |
26 | 26 | |
27 | 27 | #include "stl.h" |
28 | 28 | |
29 | static void stl_rotate(float *x, float *y, float angle); | |
29 | static void stl_rotate(float *x, float *y, const double c, const double s); | |
30 | 30 | static float get_area(stl_facet *facet); |
31 | 31 | static float get_volume(stl_file *stl); |
32 | 32 | |
188 | 188 | stl_rotate_x(stl_file *stl, float angle) { |
189 | 189 | int i; |
190 | 190 | int j; |
191 | double radian_angle = (angle / 180.0) * M_PI; | |
192 | double c = cos(radian_angle); | |
193 | double s = sin(radian_angle); | |
191 | 194 | |
192 | 195 | if (stl->error) return; |
193 | 196 | |
194 | 197 | for(i = 0; i < stl->stats.number_of_facets; i++) { |
195 | 198 | for(j = 0; j < 3; j++) { |
196 | 199 | stl_rotate(&stl->facet_start[i].vertex[j].y, |
197 | &stl->facet_start[i].vertex[j].z, angle); | |
200 | &stl->facet_start[i].vertex[j].z, c, s); | |
198 | 201 | } |
199 | 202 | } |
200 | 203 | stl_get_size(stl); |
205 | 208 | stl_rotate_y(stl_file *stl, float angle) { |
206 | 209 | int i; |
207 | 210 | int j; |
211 | double radian_angle = (angle / 180.0) * M_PI; | |
212 | double c = cos(radian_angle); | |
213 | double s = sin(radian_angle); | |
208 | 214 | |
209 | 215 | if (stl->error) return; |
210 | 216 | |
211 | 217 | for(i = 0; i < stl->stats.number_of_facets; i++) { |
212 | 218 | for(j = 0; j < 3; j++) { |
213 | 219 | stl_rotate(&stl->facet_start[i].vertex[j].z, |
214 | &stl->facet_start[i].vertex[j].x, angle); | |
220 | &stl->facet_start[i].vertex[j].x, c, s); | |
215 | 221 | } |
216 | 222 | } |
217 | 223 | stl_get_size(stl); |
222 | 228 | stl_rotate_z(stl_file *stl, float angle) { |
223 | 229 | int i; |
224 | 230 | int j; |
231 | double radian_angle = (angle / 180.0) * M_PI; | |
232 | double c = cos(radian_angle); | |
233 | double s = sin(radian_angle); | |
225 | 234 | |
226 | 235 | if (stl->error) return; |
227 | 236 | |
228 | 237 | for(i = 0; i < stl->stats.number_of_facets; i++) { |
229 | 238 | for(j = 0; j < 3; j++) { |
230 | 239 | stl_rotate(&stl->facet_start[i].vertex[j].x, |
231 | &stl->facet_start[i].vertex[j].y, angle); | |
240 | &stl->facet_start[i].vertex[j].y, c, s); | |
232 | 241 | } |
233 | 242 | } |
234 | 243 | stl_get_size(stl); |
238 | 247 | |
239 | 248 | |
240 | 249 | static void |
241 | stl_rotate(float *x, float *y, float angle) { | |
242 | double r; | |
243 | double theta; | |
244 | double radian_angle; | |
245 | ||
246 | radian_angle = (angle / 180.0) * M_PI; | |
247 | ||
248 | r = sqrt((*x **x) + (*y **y)); | |
249 | theta = atan2(*y, *x); | |
250 | *x = r * cos(theta + radian_angle); | |
251 | *y = r * sin(theta + radian_angle); | |
250 | stl_rotate(float *x, float *y, const double c, const double s) { | |
251 | double xold = *x; | |
252 | double yold = *y; | |
253 | *x = float(c * xold - s * yold); | |
254 | *y = float(s * xold + c * yold); | |
252 | 255 | } |
253 | 256 | |
254 | 257 | extern void |
5 | 5 | #ifdef CLIPPER_UTILS_DEBUG |
6 | 6 | #include "SVG.hpp" |
7 | 7 | #endif /* CLIPPER_UTILS_DEBUG */ |
8 | ||
9 | #include <Shiny/Shiny.h> | |
8 | 10 | |
9 | 11 | namespace Slic3r { |
10 | 12 | |
27 | 29 | |
28 | 30 | void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons* expolygons) |
29 | 31 | { |
30 | expolygons->clear(); | |
31 | for (int i = 0; i < polytree.ChildCount(); ++i) | |
32 | AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); | |
32 | PROFILE_FUNC(); | |
33 | expolygons->clear(); | |
34 | for (int i = 0; i < polytree.ChildCount(); ++i) | |
35 | AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); | |
33 | 36 | } |
34 | 37 | //----------------------------------------------------------- |
35 | 38 | |
37 | 40 | void |
38 | 41 | ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output) |
39 | 42 | { |
43 | PROFILE_FUNC(); | |
40 | 44 | output->points.clear(); |
41 | 45 | output->points.reserve(input.size()); |
42 | 46 | for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) |
48 | 52 | void |
49 | 53 | ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output) |
50 | 54 | { |
55 | PROFILE_FUNC(); | |
51 | 56 | output->clear(); |
52 | 57 | output->reserve(input.size()); |
53 | 58 | for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { |
60 | 65 | void |
61 | 66 | ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output) |
62 | 67 | { |
68 | PROFILE_FUNC(); | |
69 | ||
63 | 70 | // init Clipper |
64 | 71 | ClipperLib::Clipper clipper; |
65 | 72 | clipper.Clear(); |
77 | 84 | void |
78 | 85 | Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output) |
79 | 86 | { |
87 | PROFILE_FUNC(); | |
88 | ||
80 | 89 | output->clear(); |
81 | 90 | output->reserve(input.points.size()); |
82 | 91 | for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) |
86 | 95 | void |
87 | 96 | Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input, ClipperLib::Path* output) |
88 | 97 | { |
98 | PROFILE_FUNC(); | |
99 | ||
89 | 100 | output->clear(); |
90 | 101 | output->reserve(input.points.size()); |
91 | 102 | for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) |
96 | 107 | void |
97 | 108 | Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output) |
98 | 109 | { |
110 | PROFILE_FUNC(); | |
111 | ||
99 | 112 | output->clear(); |
100 | 113 | output->reserve(input.size()); |
101 | 114 | for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { |
109 | 122 | void |
110 | 123 | scaleClipperPolygon(ClipperLib::Path &polygon, const double scale) |
111 | 124 | { |
125 | PROFILE_FUNC(); | |
126 | ||
112 | 127 | for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { |
113 | 128 | //FIXME multiplication of int64_t by double! |
114 | 129 | // Replace by bit shifts? |
120 | 135 | void |
121 | 136 | scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) |
122 | 137 | { |
138 | PROFILE_FUNC(); | |
139 | ||
123 | 140 | for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) { |
124 | 141 | for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { |
125 | 142 | //FIXME multiplication of int64_t by double! |
134 | 151 | offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, |
135 | 152 | double scale, ClipperLib::JoinType joinType, double miterLimit) |
136 | 153 | { |
154 | PROFILE_FUNC(); | |
137 | 155 | // read input |
138 | 156 | ClipperLib::Paths input; |
139 | 157 | Slic3rMultiPoints_to_ClipperPaths(polygons, &input); |
148 | 166 | } else { |
149 | 167 | co.MiterLimit = miterLimit; |
150 | 168 | } |
151 | co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); | |
152 | co.Execute(*retval, (delta*scale)); | |
169 | { | |
170 | PROFILE_BLOCK(offset_AddPaths); | |
171 | co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); | |
172 | } | |
173 | { | |
174 | PROFILE_BLOCK(offset_Execute); | |
175 | co.Execute(*retval, (delta*scale)); | |
176 | } | |
153 | 177 | |
154 | 178 | // unscale output |
155 | 179 | scaleClipperPolygons(*retval, 1/scale); |
433 | 457 | offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, |
434 | 458 | const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) |
435 | 459 | { |
460 | if (delta1 * delta2 >= 0) { | |
461 | // Both deltas are the same signum | |
462 | offset(polygons, retval, delta1 + delta2, scale, joinType, miterLimit); | |
463 | return; | |
464 | } | |
436 | 465 | #ifdef CLIPPER_UTILS_DEBUG |
437 | 466 | BoundingBox bbox = get_extents(polygons); |
438 | 467 | coordf_t stroke_width = scale_(0.005); |
525 | 554 | void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, |
526 | 555 | const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) |
527 | 556 | { |
557 | PROFILE_BLOCK(_clipper_do_polygons); | |
558 | ||
528 | 559 | // read input |
529 | 560 | ClipperLib::Paths input_subject, input_clip; |
530 | 561 | Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); |
544 | 575 | clipper.Clear(); |
545 | 576 | |
546 | 577 | // add polygons |
547 | clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); | |
548 | clipper.AddPaths(input_clip, ClipperLib::ptClip, true); | |
578 | { | |
579 | PROFILE_BLOCK(_clipper_do_polygons_AddPaths); | |
580 | clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); | |
581 | clipper.AddPaths(input_clip, ClipperLib::ptClip, true); | |
582 | } | |
549 | 583 | |
550 | 584 | // perform operation |
551 | clipper.Execute(clipType, *retval, fillType, fillType); | |
585 | { | |
586 | PROFILE_BLOCK(_clipper_do_polygons_Execute); | |
587 | clipper.Execute(clipType, *retval, fillType, fillType); | |
588 | } | |
552 | 589 | } |
553 | 590 | |
554 | 591 | void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, |
555 | 592 | const Slic3r::Polygons &clip, ClipperLib::PolyTree* retval, const ClipperLib::PolyFillType fillType, |
556 | 593 | const bool safety_offset_) |
557 | 594 | { |
595 | PROFILE_BLOCK(_clipper_do_polylines); | |
596 | ||
558 | 597 | // read input |
559 | 598 | ClipperLib::Paths input_subject, input_clip; |
560 | 599 | Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); |
568 | 607 | clipper.Clear(); |
569 | 608 | |
570 | 609 | // add polygons |
571 | clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); | |
572 | clipper.AddPaths(input_clip, ClipperLib::ptClip, true); | |
610 | { | |
611 | PROFILE_BLOCK(_clipper_do_polylines_AddPaths); | |
612 | clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); | |
613 | clipper.AddPaths(input_clip, ClipperLib::ptClip, true); | |
614 | } | |
573 | 615 | |
574 | 616 | // perform operation |
575 | clipper.Execute(clipType, *retval, fillType, fillType); | |
617 | { | |
618 | PROFILE_BLOCK(_clipper_do_polylines_Execute); | |
619 | clipper.Execute(clipType, *retval, fillType, fillType); | |
620 | } | |
576 | 621 | } |
577 | 622 | |
578 | 623 | void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, |
579 | 624 | const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_) |
580 | 625 | { |
626 | PROFILE_FUNC(); | |
581 | 627 | // perform operation |
582 | 628 | ClipperLib::Paths output; |
583 | 629 | _clipper_do<ClipperLib::Paths>(clipType, subject, clip, &output, ClipperLib::pftNonZero, safety_offset_); |
589 | 635 | void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, |
590 | 636 | const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_) |
591 | 637 | { |
638 | PROFILE_FUNC(); | |
592 | 639 | // perform operation |
593 | 640 | ClipperLib::PolyTree polytree; |
594 | 641 | _clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); |
600 | 647 | void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, |
601 | 648 | const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) |
602 | 649 | { |
650 | PROFILE_FUNC(); | |
603 | 651 | // perform operation |
604 | 652 | ClipperLib::PolyTree polytree; |
605 | 653 | _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); |
885 | 933 | |
886 | 934 | void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear) |
887 | 935 | { |
936 | PROFILE_FUNC(); | |
937 | ||
888 | 938 | // convert into Clipper polygons |
889 | 939 | ClipperLib::Paths input_subject, output; |
890 | 940 | Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); |
905 | 955 | |
906 | 956 | void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear) |
907 | 957 | { |
958 | PROFILE_FUNC(); | |
959 | ||
908 | 960 | if (!preserve_collinear) { |
909 | 961 | Polygons polygons; |
910 | 962 | simplify_polygons(subject, &polygons, preserve_collinear); |
930 | 982 | |
931 | 983 | void safety_offset(ClipperLib::Paths* paths) |
932 | 984 | { |
985 | PROFILE_FUNC(); | |
986 | ||
933 | 987 | // scale input |
934 | 988 | scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE); |
935 | 989 | |
936 | 990 | // perform offset (delta = scale 1e-05) |
937 | 991 | ClipperLib::ClipperOffset co; |
938 | 992 | co.MiterLimit = 2; |
939 | co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); | |
940 | co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE); | |
993 | { | |
994 | PROFILE_BLOCK(safety_offset_AddPaths); | |
995 | co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); | |
996 | } | |
997 | { | |
998 | PROFILE_BLOCK(safety_offset_Execute); | |
999 | co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE); | |
1000 | } | |
941 | 1001 | |
942 | 1002 | // unscale output |
943 | 1003 | scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE); |
57 | 57 | |
58 | 58 | void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const |
59 | 59 | { |
60 | offset(this->polyline, &out, scale_(this->width/2) + scaled_epsilon); | |
60 | Polygons tmp; | |
61 | offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon); | |
62 | polygons_append(out, STDMOVE(tmp)); | |
61 | 63 | } |
62 | 64 | |
63 | 65 | void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const |
65 | 67 | // Instantiating the Flow class to get the line spacing. |
66 | 68 | // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. |
67 | 69 | Flow flow(this->width, this->height, 0.f, this->is_bridge()); |
68 | offset(this->polyline, &out, 0.5f * flow.scaled_spacing() + scaled_epsilon); | |
70 | Polygons tmp; | |
71 | offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon); | |
72 | polygons_append(out, STDMOVE(tmp)); | |
69 | 73 | } |
70 | 74 | |
71 | 75 | bool |
263 | 263 | if (rate < 40.f) { |
264 | 264 | printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n", |
265 | 265 | rate, |
266 | line_idx, | |
266 | int(line_idx), | |
267 | 267 | sqrt(len2), sqrt((diff[3]*diff[3])/len2), |
268 | 268 | m_current_pos[0], m_current_pos[1], m_current_pos[2], |
269 | 269 | new_pos[0], new_pos[1], new_pos[2]); |
503 | 503 | } |
504 | 504 | cellsorder.insert(cellsorder.begin() + low, ArrangeItemIndex(index, c)); |
505 | 505 | } |
506 | ENDSORT: true; | |
506 | ENDSORT: ; | |
507 | 507 | } |
508 | 508 | } |
509 | 509 |
102 | 102 | } else { |
103 | 103 | Polygons slices_p; |
104 | 104 | FOREACH_LAYERREGION(this, layerm) { |
105 | Polygons region_slices_p = (*layerm)->slices; | |
106 | slices_p.insert(slices_p.end(), region_slices_p.begin(), region_slices_p.end()); | |
105 | polygons_append(slices_p, to_polygons((*layerm)->slices)); | |
107 | 106 | } |
108 | 107 | union_(slices_p, &slices); |
109 | 108 | } |
122 | 121 | Slic3r::Geometry::chained_path(ordering_points, order); |
123 | 122 | |
124 | 123 | // populate slices vector |
125 | for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it) { | |
126 | this->slices.expolygons.push_back(slices[*it]); | |
127 | } | |
124 | for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it) | |
125 | this->slices.expolygons.push_back(STDMOVE(slices[*it])); | |
128 | 126 | } |
129 | 127 | |
130 | 128 | void |
131 | 129 | Layer::merge_slices() |
132 | 130 | { |
133 | FOREACH_LAYERREGION(this, layerm) { | |
134 | (*layerm)->merge_slices(); | |
131 | if (this->regions.size() == 1) { | |
132 | // Optimization, also more robust. Don't merge classified pieces of layerm->slices, | |
133 | // but use the non-split islands of a layer. For a single region print, these shall be equal. | |
134 | this->regions.front()->slices.surfaces.clear(); | |
135 | surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal); | |
136 | } else { | |
137 | FOREACH_LAYERREGION(this, layerm) { | |
138 | ExPolygons expp; | |
139 | // without safety offset, artifacts are generated (GH #2494) | |
140 | union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true); | |
141 | (*layerm)->slices.surfaces.clear(); | |
142 | surfaces_append((*layerm)->slices.surfaces, expp, stInternal); | |
143 | } | |
135 | 144 | } |
136 | 145 | } |
137 | 146 | |
199 | 208 | |
200 | 209 | if (layerms.size() == 1) { // optimization |
201 | 210 | (*layerm)->fill_surfaces.surfaces.clear(); |
202 | (*layerm)->perimeter_surfaces.surfaces.clear(); | |
203 | (*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->perimeter_surfaces, &(*layerm)->fill_surfaces); | |
204 | this->perimeter_expolygons.expolygons.clear(); | |
205 | for (Surfaces::const_iterator it = (*layerm)->perimeter_surfaces.surfaces.begin(); it != (*layerm)->perimeter_surfaces.surfaces.end(); ++ it) | |
206 | this->perimeter_expolygons.expolygons.push_back(it->expolygon); | |
211 | (*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces); | |
212 | (*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces); | |
207 | 213 | } else { |
208 | // group slices (surfaces) according to number of extra perimeters | |
209 | std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ] | |
210 | for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) { | |
211 | for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) { | |
212 | slices[s->extra_perimeters].push_back(*s); | |
214 | SurfaceCollection new_slices; | |
215 | { | |
216 | // group slices (surfaces) according to number of extra perimeters | |
217 | std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ] | |
218 | for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) { | |
219 | for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) { | |
220 | slices[s->extra_perimeters].push_back(*s); | |
221 | } | |
213 | 222 | } |
214 | } | |
215 | ||
216 | // merge the surfaces assigned to each group | |
217 | SurfaceCollection new_slices; | |
218 | for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) { | |
219 | ExPolygons expp = union_ex(it->second, true); | |
220 | for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) { | |
221 | Surface s = it->second.front(); // clone type and extra_perimeters | |
222 | s.expolygon = *ex; | |
223 | new_slices.surfaces.push_back(s); | |
224 | } | |
223 | // merge the surfaces assigned to each group | |
224 | for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) | |
225 | surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front()); | |
225 | 226 | } |
226 | 227 | |
227 | 228 | // make perimeters |
228 | SurfaceCollection perimeter_surfaces; | |
229 | 229 | SurfaceCollection fill_surfaces; |
230 | (*layerm)->make_perimeters(new_slices, &perimeter_surfaces, &fill_surfaces); | |
231 | // Copy the perimeter surfaces to the layer's surfaces before splitting them into the regions. | |
232 | this->perimeter_expolygons.expolygons.clear(); | |
233 | for (Surfaces::const_iterator it = perimeter_surfaces.surfaces.begin(); it != perimeter_surfaces.surfaces.end(); ++ it) | |
234 | this->perimeter_expolygons.expolygons.push_back(it->expolygon); | |
230 | (*layerm)->make_perimeters(new_slices, &fill_surfaces); | |
235 | 231 | |
236 | 232 | // assign fill_surfaces to each layer |
237 | 233 | if (!fill_surfaces.surfaces.empty()) { |
238 | 234 | for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) { |
239 | 235 | // Separate the fill surfaces. |
240 | ExPolygons expp = intersection_ex( | |
241 | fill_surfaces, | |
242 | (*l)->slices | |
243 | ); | |
236 | ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); | |
237 | (*l)->fill_expolygons = expp; | |
244 | 238 | (*l)->fill_surfaces.surfaces.clear(); |
245 | ||
246 | for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) { | |
247 | Surface s = fill_surfaces.surfaces.front(); // clone type and extra_perimeters | |
248 | s.expolygon = *ex; | |
249 | (*l)->fill_surfaces.surfaces.push_back(s); | |
250 | } | |
251 | ||
252 | // Separate the perimeter surfaces. | |
253 | expp = intersection_ex( | |
254 | perimeter_surfaces, | |
255 | (*l)->slices | |
256 | ); | |
257 | (*l)->perimeter_surfaces.surfaces.clear(); | |
258 | ||
259 | for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) { | |
260 | Surface s = fill_surfaces.surfaces.front(); // clone type and extra_perimeters | |
261 | s.expolygon = *ex; | |
262 | (*l)->perimeter_surfaces.surfaces.push_back(s); | |
263 | } | |
239 | surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front()); | |
264 | 240 | } |
265 | 241 | } |
266 | 242 | } |
35 | 35 | // collection of extrusion paths/loops filling gaps |
36 | 36 | ExtrusionEntityCollection thin_fills; |
37 | 37 | |
38 | // Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature") | |
39 | // and for re-starting of infills. | |
40 | ExPolygons fill_expolygons; | |
38 | 41 | // collection of surfaces for infill generation |
39 | SurfaceCollection fill_surfaces; | |
42 | SurfaceCollection fill_surfaces; | |
40 | 43 | |
41 | 44 | // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces). |
42 | 45 | // While not necessary, the memory consumption is meager and it speeds up calculation. |
59 | 62 | ExtrusionEntityCollection fills; |
60 | 63 | |
61 | 64 | Flow flow(FlowRole role, bool bridge = false, double width = -1) const; |
62 | void merge_slices(); | |
63 | 65 | void slices_to_fill_surfaces_clipped(); |
64 | 66 | void prepare_fill_surfaces(); |
65 | void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces); | |
67 | void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); | |
66 | 68 | void process_external_surfaces(const Layer* lower_layer); |
67 | 69 | double infill_area_threshold() const; |
68 | 70 | |
102 | 104 | |
103 | 105 | // collection of expolygons generated by slicing the original geometry; |
104 | 106 | // also known as 'islands' (all regions and surface types are merged here) |
107 | // The slices are chained by the shortest traverse distance and this traversal | |
108 | // order will be recovered by the G-code generator. | |
105 | 109 | ExPolygonCollection slices; |
106 | // Surfaces of the perimeters including their gap fill. | |
107 | ExPolygonCollection perimeter_expolygons; | |
108 | ||
109 | 110 | |
110 | 111 | size_t region_count() const; |
111 | 112 | const LayerRegion* get_region(int idx) const { return this->regions.at(idx); } |
40 | 40 | ); |
41 | 41 | } |
42 | 42 | |
43 | void | |
44 | LayerRegion::merge_slices() | |
45 | { | |
46 | ExPolygons expp; | |
47 | // without safety offset, artifacts are generated (GH #2494) | |
48 | union_(to_polygons(STDMOVE(this->slices.surfaces)), &expp, true); | |
49 | this->slices.surfaces.clear(); | |
50 | surfaces_append(this->slices.surfaces, expp, stInternal); | |
51 | } | |
52 | ||
53 | 43 | // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. |
54 | 44 | void LayerRegion::slices_to_fill_surfaces_clipped() |
55 | 45 | { |
57 | 47 | // in place. However we're now only using its boundaries (which are invariant) |
58 | 48 | // so we're safe. This guarantees idempotence of prepare_infill() also in case |
59 | 49 | // that combine_infill() turns some fill_surface into VOID surfaces. |
60 | Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces)); | |
50 | // Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces)); | |
51 | Polygons fill_boundaries = to_polygons(this->fill_expolygons); | |
61 | 52 | this->fill_surfaces.surfaces.clear(); |
62 | 53 | for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface) |
63 | 54 | surfaces_append( |
67 | 58 | } |
68 | 59 | |
69 | 60 | void |
70 | LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces) | |
61 | LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) | |
71 | 62 | { |
72 | 63 | this->perimeters.clear(); |
73 | 64 | this->thin_fills.clear(); |
84 | 75 | // output: |
85 | 76 | &this->perimeters, |
86 | 77 | &this->thin_fills, |
87 | perimeter_surfaces, | |
88 | 78 | fill_surfaces |
89 | 79 | ); |
90 | 80 | |
125 | 115 | // Internal surfaces, not grown. |
126 | 116 | Surfaces internal; |
127 | 117 | // Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed. |
118 | //FIXME if non zero infill, then fill_boundaries could be cheaply initialized from layerm->fill_expolygons. | |
128 | 119 | Polygons fill_boundaries; |
129 | 120 | |
130 | 121 | // Collect top surfaces and internal surfaces. |
366 | 357 | |
367 | 358 | // if no solid layers are requested, turn top/bottom surfaces to internal |
368 | 359 | if (this->region()->config.top_solid_layers == 0) { |
369 | for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { | |
370 | if (surface->surface_type == stTop) { | |
371 | if (this->layer()->object()->config.infill_only_where_needed) { | |
372 | surface->surface_type = stInternalVoid; | |
373 | } else { | |
374 | surface->surface_type = stInternal; | |
375 | } | |
376 | } | |
377 | } | |
360 | for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) | |
361 | if (surface->surface_type == stTop) | |
362 | surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ? | |
363 | stInternalVoid : stInternal; | |
378 | 364 | } |
379 | 365 | if (this->region()->config.bottom_solid_layers == 0) { |
380 | 366 | for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { |
472 | 472 | void |
473 | 473 | ModelObject::update_bounding_box() |
474 | 474 | { |
475 | this->_bounding_box = this->mesh().bounding_box(); | |
475 | // this->_bounding_box = this->mesh().bounding_box(); | |
476 | BoundingBoxf3 raw_bbox; | |
477 | for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { | |
478 | if ((*v)->modifier) continue; | |
479 | raw_bbox.merge((*v)->mesh.bounding_box()); | |
480 | } | |
481 | BoundingBoxf3 bb; | |
482 | for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) | |
483 | bb.merge((*i)->transform_bounding_box(raw_bbox)); | |
484 | this->_bounding_box = bb; | |
476 | 485 | this->_bounding_box_valid = true; |
477 | 486 | } |
478 | 487 | |
508 | 517 | BoundingBoxf3 bb; |
509 | 518 | for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { |
510 | 519 | if ((*v)->modifier) continue; |
511 | TriangleMesh mesh = (*v)->mesh; | |
512 | ||
513 | 520 | if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); |
514 | this->instances.front()->transform_mesh(&mesh, true); | |
515 | ||
516 | bb.merge(mesh.bounding_box()); | |
521 | bb.merge(this->instances.front()->transform_mesh_bounding_box(&(*v)->mesh, true)); | |
517 | 522 | } |
518 | 523 | return bb; |
519 | 524 | } |
522 | 527 | BoundingBoxf3 |
523 | 528 | ModelObject::instance_bounding_box(size_t instance_idx) const |
524 | 529 | { |
525 | TriangleMesh mesh = this->raw_mesh(); | |
526 | this->instances[instance_idx]->transform_mesh(&mesh); | |
527 | return mesh.bounding_box(); | |
530 | BoundingBoxf3 bb; | |
531 | for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { | |
532 | if ((*v)->modifier) continue; | |
533 | bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&(*v)->mesh, true)); | |
534 | } | |
535 | return bb; | |
528 | 536 | } |
529 | 537 | |
530 | 538 | void |
532 | 540 | { |
533 | 541 | // calculate the displacements needed to |
534 | 542 | // center this object around the origin |
535 | BoundingBoxf3 bb = this->raw_mesh().bounding_box(); | |
543 | BoundingBoxf3 bb; | |
544 | for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) | |
545 | if (! (*v)->modifier) | |
546 | bb.merge((*v)->mesh.bounding_box()); | |
536 | 547 | |
537 | 548 | // first align to origin on XYZ |
538 | 549 | Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z); |
774 | 785 | mesh->translate(this->offset.x, this->offset.y, 0); |
775 | 786 | } |
776 | 787 | |
788 | BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const | |
789 | { | |
790 | // rotate around mesh origin | |
791 | double c = cos(this->rotation); | |
792 | double s = sin(this->rotation); | |
793 | BoundingBoxf3 bbox; | |
794 | for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) { | |
795 | const stl_facet &facet = mesh->stl.facet_start[i]; | |
796 | for (int j = 0; j < 3; ++ j) { | |
797 | stl_vertex v = facet.vertex[j]; | |
798 | double xold = v.x; | |
799 | double yold = v.y; | |
800 | v.x = float(c * xold - s * yold); | |
801 | v.y = float(s * xold + c * yold); | |
802 | v.x *= float(this->scaling_factor); | |
803 | v.y *= float(this->scaling_factor); | |
804 | v.z *= float(this->scaling_factor); | |
805 | if (!dont_translate) { | |
806 | v.x += this->offset.x; | |
807 | v.y += this->offset.y; | |
808 | } | |
809 | bbox.merge(Pointf3(v.x, v.y, v.z)); | |
810 | } | |
811 | } | |
812 | return bbox; | |
813 | } | |
814 | ||
815 | BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const | |
816 | { | |
817 | // rotate around mesh origin | |
818 | double c = cos(this->rotation); | |
819 | double s = sin(this->rotation); | |
820 | Pointf3 pts[4] = { | |
821 | bbox.min, | |
822 | bbox.max, | |
823 | Pointf3(bbox.min.x, bbox.max.y, bbox.min.z), | |
824 | Pointf3(bbox.max.x, bbox.min.y, bbox.max.z) | |
825 | }; | |
826 | BoundingBoxf3 out; | |
827 | for (int i = 0; i < 4; ++ i) { | |
828 | Pointf3 &v = pts[i]; | |
829 | double xold = v.x; | |
830 | double yold = v.y; | |
831 | v.x = float(c * xold - s * yold); | |
832 | v.y = float(s * xold + c * yold); | |
833 | v.x *= this->scaling_factor; | |
834 | v.y *= this->scaling_factor; | |
835 | v.z *= this->scaling_factor; | |
836 | if (!dont_translate) { | |
837 | v.x += this->offset.x; | |
838 | v.y += this->offset.y; | |
839 | } | |
840 | out.merge(v); | |
841 | } | |
842 | return out; | |
843 | } | |
844 | ||
777 | 845 | void |
778 | 846 | ModelInstance::transform_polygon(Polygon* polygon) const |
779 | 847 | { |
214 | 214 | |
215 | 215 | // To be called on an external mesh |
216 | 216 | void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; |
217 | // Calculate a bounding box of a transformed mesh. To be called on an external mesh. | |
218 | BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const; | |
219 | // Transform an external bounding box. | |
220 | BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; | |
217 | 221 | // To be called on an external polygon. It does not translate the polygon, only rotates and scales. |
218 | 222 | void transform_polygon(Polygon* polygon) const; |
219 | 223 |
119 | 119 | // from the line width of the infill? |
120 | 120 | coord_t distance = (i == 1) ? ext_pspacing2 : pspacing; |
121 | 121 | |
122 | ||
123 | //FIXME Vojtech: Why there is a special case for the thin walls? | |
124 | // Gap fill is active all the time anyway and this is not the outer perimeter. | |
125 | // if (this->config->thin_walls) { | |
126 | if (false) { | |
122 | if (this->config->thin_walls) { | |
123 | // This path will ensure, that the perimeters do not overfill, as in | |
124 | // prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters | |
125 | // excessively, creating gaps, which then need to be filled in by the not very | |
126 | // reliable gap fill algorithm. | |
127 | // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than | |
128 | // the original. | |
127 | 129 | offsets = offset2( |
128 | 130 | last, |
129 | 131 | -(distance + min_spacing/2 - 1), |
130 | 132 | +(min_spacing/2 - 1) |
131 | 133 | ); |
132 | 134 | } else { |
135 | // If "detect thin walls" is not enabled, this paths will be entered, which | |
136 | // leads to overflows, as in prusa3d/Slic3r GH #32 | |
133 | 137 | offsets = offset( |
134 | 138 | last, |
135 | 139 | -distance |
241 | 245 | if (!entities.empty()) |
242 | 246 | this->loops->append(entities); |
243 | 247 | } // for each loop of an island |
244 | ||
245 | { | |
246 | //FIXME how about the gaps? | |
247 | // Calculate the region of surface->expolygon covered by the perimeters and their gap fills. | |
248 | // The perimeters will later be used to calculate the object skin. | |
249 | ExPolygons expp = diff_ex((Polygons)surface->expolygon, last, true); | |
250 | for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) | |
251 | this->perimeter_surfaces->surfaces.push_back(Surface(stPerimeter, *ex)); | |
252 | } | |
253 | 248 | |
254 | 249 | // fill gaps |
255 | 250 | if (!gaps.empty()) { |
317 | 312 | |
318 | 313 | // collapse too narrow infill areas |
319 | 314 | coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); |
320 | expp = offset2_ex( | |
321 | pp, | |
322 | -inset -min_perimeter_infill_spacing/2, | |
323 | +min_perimeter_infill_spacing/2 | |
324 | ); | |
325 | 315 | |
326 | 316 | // append infill areas to fill_surfaces |
327 | for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) | |
328 | this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex)); // use a bogus surface type | |
317 | surfaces_append( | |
318 | this->fill_surfaces->surfaces, | |
319 | offset2_ex( | |
320 | pp, | |
321 | -inset -min_perimeter_infill_spacing/2, | |
322 | +min_perimeter_infill_spacing/2), | |
323 | stInternal); | |
329 | 324 | } |
330 | 325 | } // for each island |
331 | 326 | } |
51 | 51 | // Outputs: |
52 | 52 | ExtrusionEntityCollection* loops; |
53 | 53 | ExtrusionEntityCollection* gap_fill; |
54 | SurfaceCollection* perimeter_surfaces; | |
55 | 54 | SurfaceCollection* fill_surfaces; |
56 | 55 | |
57 | 56 | PerimeterGenerator( |
67 | 66 | ExtrusionEntityCollection* loops, |
68 | 67 | // Gaps without the thin walls |
69 | 68 | ExtrusionEntityCollection* gap_fill, |
70 | // Perimeters including their gap fills | |
71 | SurfaceCollection* perimeter_surfaces, | |
72 | 69 | // Infills without the gap fills |
73 | 70 | SurfaceCollection* fill_surfaces) |
74 | 71 | : slices(slices), lower_slices(NULL), layer_height(layer_height), |
75 | 72 | layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), |
76 | 73 | overhang_flow(flow), solid_infill_flow(flow), |
77 | 74 | config(config), object_config(object_config), print_config(print_config), |
78 | loops(loops), gap_fill(gap_fill), perimeter_surfaces(perimeter_surfaces), fill_surfaces(fill_surfaces), | |
75 | loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), | |
79 | 76 | _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1) |
80 | 77 | {}; |
81 | 78 | void process(); |
2 | 2 | #include "ClipperUtils.hpp" |
3 | 3 | #include "Geometry.hpp" |
4 | 4 | #include "SVG.hpp" |
5 | ||
6 | #include <Shiny/Shiny.h> | |
5 | 7 | |
6 | 8 | namespace Slic3r { |
7 | 9 | |
310 | 312 | } |
311 | 313 | |
312 | 314 | // This function analyzes slices of a region (SurfaceCollection slices). |
313 | // Each slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. | |
315 | // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. | |
314 | 316 | // Initially all slices are of type S_TYPE_INTERNAL. |
315 | 317 | // Slices are compared against the top / bottom slices and regions and classified to the following groups: |
316 | 318 | // S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill. |
322 | 324 | { |
323 | 325 | // Slic3r::debugf "Detecting solid surfaces...\n"; |
324 | 326 | for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { |
325 | // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. | |
327 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | |
326 | 328 | for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) { |
327 | 329 | LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); |
328 | layerm->slices_to_fill_surfaces_clipped(); | |
329 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | |
330 | 330 | layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); |
331 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | |
332 | 331 | } |
332 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | |
333 | 333 | |
334 | 334 | for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) { |
335 | 335 | Layer *layer = this->layers[idx_layer]; |
481 | 481 | } |
482 | 482 | } |
483 | 483 | |
484 | struct DiscoverVerticalShellsCacheEntry | |
485 | { | |
486 | DiscoverVerticalShellsCacheEntry() : valid(false) {} | |
487 | // Collected polygons, offsetted | |
488 | Polygons slices; | |
489 | Polygons fill_surfaces; | |
490 | // Is this cache entry valid? | |
491 | bool valid; | |
492 | }; | |
493 | ||
484 | 494 | void |
485 | 495 | PrintObject::discover_vertical_shells() |
486 | 496 | { |
497 | PROFILE_FUNC(); | |
498 | ||
499 | const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; | |
500 | ||
487 | 501 | for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { |
488 | if (! this->_print->regions[idx_region]->config.ensure_vertical_shell_thickness.value) | |
502 | PROFILE_BLOCK(discover_vertical_shells_region); | |
503 | ||
504 | const PrintRegion ®ion = *this->_print->get_region(idx_region); | |
505 | if (! region.config.ensure_vertical_shell_thickness.value) | |
506 | // This region will be handled by discover_horizontal_shells(). | |
489 | 507 | continue; |
490 | for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) { | |
508 | int n_extra_top_layers = std::max(0, region.config.top_solid_layers.value - 1); | |
509 | int n_extra_bottom_layers = std::max(0, region.config.bottom_solid_layers.value - 1); | |
510 | if (n_extra_top_layers + n_extra_bottom_layers == 0) | |
511 | // Zero or 1 layer, there is no additional vertical wall thickness enforced. | |
512 | continue; | |
513 | // Cyclic buffers of pre-calculated offsetted top/bottom surfaces. | |
514 | std::vector<DiscoverVerticalShellsCacheEntry> cache_top_regions(n_extra_top_layers, DiscoverVerticalShellsCacheEntry()); | |
515 | std::vector<DiscoverVerticalShellsCacheEntry> cache_bottom_regions(n_extra_bottom_layers, DiscoverVerticalShellsCacheEntry()); | |
516 | for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) | |
517 | { | |
518 | PROFILE_BLOCK(discover_vertical_shells_region_layer); | |
519 | ||
491 | 520 | Layer *layer = this->layers[idx_layer]; |
492 | 521 | LayerRegion *layerm = layer->get_region(idx_region); |
493 | 522 | Flow solid_infill_flow = layerm->flow(frSolidInfill); |
494 | 523 | coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); |
495 | 524 | // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. |
496 | 525 | Polygons shell; |
526 | Polygons holes; | |
497 | 527 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING |
498 | 528 | ExPolygons shell_ex; |
499 | 529 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ |
500 | 530 | float min_perimeter_infill_spacing = float(infill_line_spacing) * 1.05f; |
501 | 531 | if (1) |
502 | 532 | { |
533 | PROFILE_BLOCK(discover_vertical_shells_region_layer_collect); | |
503 | 534 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING |
504 | 535 | { |
505 | 536 | static size_t idx = 0; |
506 | 537 | SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box()); |
507 | for (int n = (int)idx_layer - layerm->region()->config.bottom_solid_layers + 1; n < (int)idx_layer + layerm->region()->config.top_solid_layers; ++ n) { | |
538 | for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { | |
508 | 539 | if (n < 0 || n >= (int)this->layers.size()) |
509 | 540 | continue; |
510 | 541 | ExPolygons &expolys = this->layers[n]->perimeter_expolygons; |
523 | 554 | ++ idx; |
524 | 555 | } |
525 | 556 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ |
526 | SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; | |
527 | for (int n = (int)idx_layer - layerm->region()->config.bottom_solid_layers + 1; n < (int)idx_layer + layerm->region()->config.top_solid_layers; ++ n) | |
557 | // Reset the top / bottom inflated regions caches of entries, which are out of the moving window. | |
558 | if (n_extra_top_layers > 0) | |
559 | cache_top_regions[idx_layer % n_extra_top_layers].valid = false; | |
560 | if (n_extra_bottom_layers > 0 && idx_layer > 0) | |
561 | cache_bottom_regions[(idx_layer - 1) % n_extra_bottom_layers].valid = false; | |
562 | bool hole_first = true; | |
563 | for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) | |
528 | 564 | if (n >= 0 && n < (int)this->layers.size()) { |
529 | 565 | Layer &neighbor_layer = *this->layers[n]; |
530 | 566 | LayerRegion &neighbor_region = *neighbor_layer.get_region(int(idx_region)); |
531 | polygons_append(shell, neighbor_layer.perimeter_expolygons.expolygons); | |
567 | Polygons newholes; | |
568 | for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) | |
569 | polygons_append(newholes, to_polygons(neighbor_layer.get_region(idx_region)->fill_expolygons)); | |
570 | if (hole_first) { | |
571 | hole_first = false; | |
572 | polygons_append(holes, STDMOVE(newholes)); | |
573 | } | |
574 | else if (! holes.empty()) { | |
575 | holes = intersection(holes, newholes); | |
576 | } | |
577 | size_t n_shell_old = shell.size(); | |
532 | 578 | if (n > int(idx_layer)) { |
533 | 579 | // Collect top surfaces. |
534 | polygons_append(shell, offset(to_expolygons(neighbor_region.slices.filter_by_type(stTop)), min_perimeter_infill_spacing)); | |
535 | polygons_append(shell, offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing)); | |
580 | DiscoverVerticalShellsCacheEntry &cache = cache_top_regions[n % n_extra_top_layers]; | |
581 | if (! cache.valid) { | |
582 | cache.valid = true; | |
583 | // neighbor_region.slices contain the source top regions, | |
584 | // so one would think that they encompass the top fill_surfaces. But the fill_surfaces could have been | |
585 | // expanded before, therefore they may protrude out of neighbor_region.slices's top surfaces. | |
586 | //FIXME one should probably use the cummulative top surfaces over all regions here. | |
587 | cache.slices = offset(to_expolygons(neighbor_region.slices.filter_by_type(stTop)), min_perimeter_infill_spacing); | |
588 | cache.fill_surfaces = offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing); | |
589 | } | |
590 | polygons_append(shell, cache.slices); | |
591 | polygons_append(shell, cache.fill_surfaces); | |
536 | 592 | } |
537 | 593 | else if (n < int(idx_layer)) { |
538 | 594 | // Collect bottom and bottom bridge surfaces. |
539 | polygons_append(shell, offset(to_expolygons(neighbor_region.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); | |
540 | polygons_append(shell, offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); | |
595 | DiscoverVerticalShellsCacheEntry &cache = cache_bottom_regions[n % n_extra_bottom_layers]; | |
596 | if (! cache.valid) { | |
597 | cache.valid = true; | |
598 | //FIXME one should probably use the cummulative top surfaces over all regions here. | |
599 | cache.slices = offset(to_expolygons(neighbor_region.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing); | |
600 | cache.fill_surfaces = offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing); | |
601 | } | |
602 | polygons_append(shell, cache.slices); | |
603 | polygons_append(shell, cache.fill_surfaces); | |
541 | 604 | } |
605 | // Running the union_ using the Clipper library piece by piece is cheaper | |
606 | // than running the union_ all at once. | |
607 | if (n_shell_old < shell.size()) | |
608 | shell = union_(shell, false); | |
542 | 609 | } |
543 | 610 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING |
544 | 611 | { |
549 | 616 | svg.Close(); |
550 | 617 | } |
551 | 618 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ |
552 | shell = union_(shell, true); | |
619 | #if 0 | |
620 | { | |
621 | PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); | |
622 | // shell = union_(shell, true); | |
623 | shell = union_(shell, false); | |
624 | } | |
625 | #endif | |
553 | 626 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING |
554 | 627 | shell_ex = union_ex(shell, true); |
555 | 628 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ |
556 | 629 | } |
557 | 630 | |
558 | if (shell.empty()) | |
559 | continue; | |
631 | //if (shell.empty()) | |
632 | // continue; | |
560 | 633 | |
561 | 634 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING |
562 | 635 | { |
602 | 675 | const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid }; |
603 | 676 | const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2)); |
604 | 677 | shell = intersection(shell, polygonsInternal, true); |
678 | polygons_append(shell, diff(polygonsInternal, holes)); | |
605 | 679 | if (shell.empty()) |
606 | 680 | continue; |
607 | 681 | |
689 | 763 | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ |
690 | 764 | } // for each layer |
691 | 765 | } // for each region |
766 | ||
767 | // Write the profiler measurements to file | |
768 | PROFILE_UPDATE(); | |
769 | PROFILE_OUTPUT(debug_out_path("discover_vertical_shells-profile.txt").c_str()); | |
692 | 770 | } |
693 | 771 | |
694 | 772 | /* This method applies bridge flow to the first internal solid layer above |
96 | 96 | return polygons; |
97 | 97 | } |
98 | 98 | |
99 | inline ExPolygons to_expolygons(const Surfaces &src) | |
100 | { | |
101 | ExPolygons expolygons; | |
102 | expolygons.reserve(src.size()); | |
103 | for (Surfaces::const_iterator it = src.begin(); it != src.end(); ++it) | |
104 | expolygons.push_back(it->expolygon); | |
105 | return expolygons; | |
106 | } | |
107 | ||
99 | 108 | inline ExPolygons to_expolygons(const SurfacesPtr &src) |
100 | 109 | { |
101 | 110 | ExPolygons expolygons; |
57 | 57 | void |
58 | 58 | TriangleMesh::swap(TriangleMesh &other) |
59 | 59 | { |
60 | std::swap(this->stl, other.stl); | |
61 | std::swap(this->repaired, other.repaired); | |
62 | std::swap(this->stl.facet_start, other.stl.facet_start); | |
63 | std::swap(this->stl.neighbors_start, other.stl.neighbors_start); | |
64 | std::swap(this->stl.v_indices, other.stl.v_indices); | |
65 | std::swap(this->stl.v_shared, other.stl.v_shared); | |
60 | std::swap(this->stl, other.stl); | |
61 | std::swap(this->repaired, other.repaired); | |
66 | 62 | } |
67 | 63 | |
68 | 64 | TriangleMesh::~TriangleMesh() { |
9 | 9 | #include <stdarg.h> |
10 | 10 | |
11 | 11 | #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" |
12 | #define SLIC3R_VERSION "1.3.0-dev" | |
12 | #define SLIC3R_VERSION "1.31.6" | |
13 | 13 | |
14 | 14 | //FIXME This epsilon value is used for many non-related purposes: |
15 | 15 | // For a threshold of a squared Euclidean distance, |
39 | 39 | #include "ppport.h" |
40 | 40 | #undef do_open |
41 | 41 | #undef do_close |
42 | #undef bind | |
43 | #undef seed | |
42 | 44 | #ifdef _MSC_VER |
43 | 45 | // Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32 |
44 | 46 | #undef send |
45 | 47 | #undef connect |
46 | #undef bind | |
47 | 48 | #endif /* _MSC_VER */ |
48 | 49 | } |
49 | 50 | #endif |
28 | 28 | long x_max() %code{% RETVAL = THIS->max.x; %}; |
29 | 29 | long y_min() %code{% RETVAL = THIS->min.y; %}; |
30 | 30 | long y_max() %code{% RETVAL = THIS->max.y; %}; |
31 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d;%d,%d", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; | |
31 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; | |
32 | 32 | |
33 | 33 | %{ |
34 | 34 |
29 | 29 | |
30 | 30 | Clone<Flow> flow(FlowRole role, bool bridge = false, double width = -1) |
31 | 31 | %code%{ RETVAL = THIS->flow(role, bridge, width); %}; |
32 | void merge_slices(); | |
33 | 32 | void prepare_fill_surfaces(); |
34 | void make_perimeters(SurfaceCollection* slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces) | |
35 | %code%{ THIS->make_perimeters(*slices, perimeter_surfaces, fill_surfaces); %}; | |
33 | void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces) | |
34 | %code%{ THIS->make_perimeters(*slices, fill_surfaces); %}; | |
36 | 35 | double infill_area_threshold(); |
37 | 36 | |
38 | 37 | void export_region_slices_to_svg(const char *path); |
79 | 78 | Ref<ExPolygonCollection> slices() |
80 | 79 | %code%{ RETVAL = &THIS->slices; %}; |
81 | 80 | |
82 | Ref<ExPolygonCollection> perimeter_expolygons() | |
83 | %code%{ RETVAL = &THIS->perimeter_expolygons; %}; | |
84 | ||
85 | 81 | int ptr() |
86 | 82 | %code%{ RETVAL = (int)(intptr_t)THIS; %}; |
87 | 83 |
9 | 9 | StaticPrintConfig* region_config, StaticPrintConfig* object_config, |
10 | 10 | StaticPrintConfig* print_config, ExtrusionEntityCollection* loops, |
11 | 11 | ExtrusionEntityCollection* gap_fill, |
12 | SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces) | |
12 | SurfaceCollection* fill_surfaces) | |
13 | 13 | %code{% RETVAL = new PerimeterGenerator(slices, layer_height, *flow, |
14 | 14 | dynamic_cast<PrintRegionConfig*>(region_config), |
15 | 15 | dynamic_cast<PrintObjectConfig*>(object_config), |
16 | 16 | dynamic_cast<PrintConfig*>(print_config), |
17 | loops, gap_fill, perimeter_surfaces, fill_surfaces); %}; | |
17 | loops, gap_fill, fill_surfaces); %}; | |
18 | 18 | ~PerimeterGenerator(); |
19 | 19 | |
20 | 20 | void set_lower_slices(ExPolygonCollection* lower_slices) |
48 | 48 | %code{% RETVAL = new Point(THIS->negative()); %}; |
49 | 49 | bool coincides_with_epsilon(Point* point) |
50 | 50 | %code{% RETVAL = THIS->coincides_with_epsilon(*point); %}; |
51 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d", THIS->x, THIS->y); RETVAL = buf; %}; | |
51 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld", THIS->x, THIS->y); RETVAL = buf; %}; | |
52 | 52 | |
53 | 53 | %{ |
54 | 54 | |
86 | 86 | %code{% RETVAL = THIS->y; %}; |
87 | 87 | long z() |
88 | 88 | %code{% RETVAL = THIS->z; %}; |
89 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d,%d", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; | |
89 | std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld,%ld", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; | |
90 | 90 | }; |
91 | 91 | |
92 | 92 | %name{Slic3r::Pointf} class Pointf { |