Merge remote-tracking branch 'origin/dev' into sla_base_pool

This commit is contained in:
tamasmeszaros 2018-08-27 09:53:52 +02:00
commit d749080261
388 changed files with 115166 additions and 3275 deletions

View file

@ -167,6 +167,7 @@ sub thread_cleanup {
*Slic3r::GUI::PresetHints::DESTROY = sub {}; *Slic3r::GUI::PresetHints::DESTROY = sub {};
*Slic3r::GUI::TabIface::DESTROY = sub {}; *Slic3r::GUI::TabIface::DESTROY = sub {};
*Slic3r::OctoPrint::DESTROY = sub {}; *Slic3r::OctoPrint::DESTROY = sub {};
*Slic3r::Duet::DESTROY = sub {};
*Slic3r::PresetUpdater::DESTROY = sub {}; *Slic3r::PresetUpdater::DESTROY = sub {};
return undef; # this prevents a "Scalars leaked" warning return undef; # this prevents a "Scalars leaked" warning
} }

View file

@ -26,6 +26,14 @@ our $appController;
our $VALUE_CHANGE_EVENT = Wx::NewEventType; our $VALUE_CHANGE_EVENT = Wx::NewEventType;
# 2) To inform about a preset selection change or a "modified" status change. # 2) To inform about a preset selection change or a "modified" status change.
our $PRESETS_CHANGED_EVENT = Wx::NewEventType; our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
# 3) To inform about a change of object selection
our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
# 4) To inform about a change of object settings
our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
# 5) To inform about a remove of object
our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
# 6) To inform about a update of the scene
our $UPDATE_SCENE_EVENT = Wx::NewEventType;
sub new { sub new {
my ($class, %params) = @_; my ($class, %params) = @_;
@ -123,6 +131,8 @@ sub new {
$self->update_ui_from_settings; $self->update_ui_from_settings;
Slic3r::GUI::update_mode();
return $self; return $self;
} }
@ -142,7 +152,12 @@ sub _init_tabpanel {
}); });
if (!$self->{no_plater}) { if (!$self->{no_plater}) {
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), L("Plater")); $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel,
event_object_selection_changed => $OBJECT_SELECTION_CHANGED_EVENT,
event_object_settings_changed => $OBJECT_SETTINGS_CHANGED_EVENT,
event_remove_object => $OBJECT_REMOVE_EVENT,
event_update_scene => $UPDATE_SCENE_EVENT,
), L("Plater"));
if (!$self->{no_controller}) { if (!$self->{no_controller}) {
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller")); $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
} }
@ -163,6 +178,10 @@ sub _init_tabpanel {
my $value = $event->GetInt(); my $value = $event->GetInt();
$self->{plater}->on_extruders_change($value); $self->{plater}->on_extruders_change($value);
} }
if ($opt_key eq 'printer_technology'){
my $value = $event->GetInt();# 0 ~ "ptFFF"; 1 ~ "ptSLA"
$self->{plater}->show_preset_comboboxes($value);
}
} }
# don't save while loading for the first time # don't save while loading for the first time
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded}; $self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
@ -175,7 +194,7 @@ sub _init_tabpanel {
my $tab = Slic3r::GUI::get_preset_tab($tab_name); my $tab = Slic3r::GUI::get_preset_tab($tab_name);
if ($self->{plater}) { if ($self->{plater}) {
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. # Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
my $presets = $tab->get_presets; my $presets = $tab->get_presets;
if (defined $presets){ if (defined $presets){
my $reload_dependent_tabs = $tab->get_dependent_tabs; my $reload_dependent_tabs = $tab->get_dependent_tabs;
@ -183,7 +202,7 @@ sub _init_tabpanel {
$self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item; $self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
if ($tab_name eq 'printer') { if ($tab_name eq 'printer') {
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
for my $tab_name_other (qw(print filament)) { for my $tab_name_other (qw(print filament sla_material)) {
# If the printer tells us that the print or filament preset has been switched or invalidated, # If the printer tells us that the print or filament preset has been switched or invalidated,
# refresh the print or filament tab page. Otherwise just refresh the combo box. # refresh the print or filament tab page. Otherwise just refresh the combo box.
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs}))) my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
@ -197,9 +216,43 @@ sub _init_tabpanel {
} }
} }
}); });
# The following event is emited by the C++ Tab implementation on object selection change.
EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
my ($self, $event) = @_;
my $obj_idx = $event->GetId;
my $child = $event->GetInt == 1 ? 1 : undef;
$self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
$self->{plater}->item_changed_selection($obj_idx);
});
# The following event is emited by the C++ GUI implementation on object settings change.
EVT_COMMAND($self, -1, $OBJECT_SETTINGS_CHANGED_EVENT, sub {
my ($self, $event) = @_;
my $line = $event->GetString;
my ($obj_idx, $parts_changed, $part_settings_changed) = split('',$line);
$self->{plater}->changed_object_settings($obj_idx, $parts_changed, $part_settings_changed);
});
# The following event is emited by the C++ GUI implementation on object remove.
EVT_COMMAND($self, -1, $OBJECT_REMOVE_EVENT, sub {
my ($self, $event) = @_;
$self->{plater}->remove();
});
# The following event is emited by the C++ GUI implementation on extruder change for object.
EVT_COMMAND($self, -1, $UPDATE_SCENE_EVENT, sub {
my ($self, $event) = @_;
$self->{plater}->update();
});
Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT); Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
$self->{options_tabs} = {}; $self->{options_tabs} = {};
for my $tab_name (qw(print filament printer)) { for my $tab_name (qw(print filament sla_material printer)) {
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name"); $self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
} }
@ -211,8 +264,14 @@ sub _init_tabpanel {
# load initial config # load initial config
my $full_config = wxTheApp->{preset_bundle}->full_config; my $full_config = wxTheApp->{preset_bundle}->full_config;
$self->{plater}->on_config_change($full_config); $self->{plater}->on_config_change($full_config);
# Show a correct number of filament fields. # Show a correct number of filament fields.
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter})); if (defined $full_config->nozzle_diameter){ # nozzle_diameter is undefined when SLA printer is selected
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
}
# Show correct preset comboboxes according to the printer_technology
$self->{plater}->show_preset_comboboxes(($full_config->printer_technology eq "FFF") ? 0 : 1);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -378,6 +378,7 @@ sub on_btn_load {
} }
} }
$self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed; $self->_parts_changed;
} }
@ -480,6 +481,7 @@ sub on_btn_delete {
$self->{parts_changed} = 1; $self->{parts_changed} = 1;
} }
$self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed; $self->_parts_changed;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

BIN
resources/icons/erase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
resources/icons/lambda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

BIN
resources/icons/lambda_.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

BIN
resources/icons/object.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
resources/icons/split.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,021 B

BIN
resources/icons/toolbar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

View file

@ -207,6 +207,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/GLGizmo.cpp ${LIBDIR}/slic3r/GUI/GLGizmo.cpp
${LIBDIR}/slic3r/GUI/GLTexture.hpp ${LIBDIR}/slic3r/GUI/GLTexture.hpp
${LIBDIR}/slic3r/GUI/GLTexture.cpp ${LIBDIR}/slic3r/GUI/GLTexture.cpp
${LIBDIR}/slic3r/GUI/GLToolbar.hpp
${LIBDIR}/slic3r/GUI/GLToolbar.cpp
${LIBDIR}/slic3r/GUI/Preferences.cpp ${LIBDIR}/slic3r/GUI/Preferences.cpp
${LIBDIR}/slic3r/GUI/Preferences.hpp ${LIBDIR}/slic3r/GUI/Preferences.hpp
${LIBDIR}/slic3r/GUI/Preset.cpp ${LIBDIR}/slic3r/GUI/Preset.cpp
@ -217,6 +219,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/PresetHints.hpp ${LIBDIR}/slic3r/GUI/PresetHints.hpp
${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.cpp
${LIBDIR}/slic3r/GUI/GUI.hpp ${LIBDIR}/slic3r/GUI/GUI.hpp
${LIBDIR}/slic3r/GUI/GUI_ObjectParts.cpp
${LIBDIR}/slic3r/GUI/GUI_ObjectParts.hpp
${LIBDIR}/slic3r/GUI/LambdaObjectDialog.cpp
${LIBDIR}/slic3r/GUI/LambdaObjectDialog.hpp
${LIBDIR}/slic3r/GUI/Tab.cpp ${LIBDIR}/slic3r/GUI/Tab.cpp
${LIBDIR}/slic3r/GUI/Tab.hpp ${LIBDIR}/slic3r/GUI/Tab.hpp
${LIBDIR}/slic3r/GUI/TabIface.cpp ${LIBDIR}/slic3r/GUI/TabIface.cpp
@ -259,8 +265,14 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp
${LIBDIR}/slic3r/Utils/PrintHostSendDialog.cpp
${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp
${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp
${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp
${LIBDIR}/slic3r/Utils/Duet.cpp
${LIBDIR}/slic3r/Utils/Duet.hpp
${LIBDIR}/slic3r/Utils/PrintHost.cpp
${LIBDIR}/slic3r/Utils/PrintHost.hpp
${LIBDIR}/slic3r/Utils/Bonjour.cpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp
${LIBDIR}/slic3r/Utils/Bonjour.hpp ${LIBDIR}/slic3r/Utils/Bonjour.hpp
${LIBDIR}/slic3r/Utils/PresetUpdater.cpp ${LIBDIR}/slic3r/Utils/PresetUpdater.cpp
@ -464,7 +476,7 @@ set(XS_XSP_FILES
${XSP_DIR}/Surface.xsp ${XSP_DIR}/Surface.xsp
${XSP_DIR}/SurfaceCollection.xsp ${XSP_DIR}/SurfaceCollection.xsp
${XSP_DIR}/TriangleMesh.xsp ${XSP_DIR}/TriangleMesh.xsp
${XSP_DIR}/Utils_OctoPrint.xsp ${XSP_DIR}/Utils_PrintHost.xsp
${XSP_DIR}/Utils_PresetUpdater.xsp ${XSP_DIR}/Utils_PresetUpdater.xsp
${XSP_DIR}/AppController.xsp ${XSP_DIR}/AppController.xsp
${XSP_DIR}/XS.xsp ${XSP_DIR}/XS.xsp
@ -513,7 +525,7 @@ if(APPLE)
# Ignore undefined symbols of the perl interpreter, they will be found in the caller image. # Ignore undefined symbols of the perl interpreter, they will be found in the caller image.
target_link_libraries(XS "-undefined dynamic_lookup") target_link_libraries(XS "-undefined dynamic_lookup")
endif() endif()
target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude) target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude qhull)
if(SLIC3R_PROFILE) if(SLIC3R_PROFILE)
target_link_libraries(XS Shiny) target_link_libraries(XS Shiny)
endif() endif()
@ -602,6 +614,10 @@ endif()
add_subdirectory(src/avrdude) add_subdirectory(src/avrdude)
add_subdirectory(src/qhull)
include_directories(${LIBDIR}/qhull/src)
message(STATUS ${LIBDIR}/qhull/src)
## REQUIRED packages ## REQUIRED packages
# Find and configure boost # Find and configure boost

View file

@ -33,16 +33,6 @@ use overload
'@{}' => sub { $_[0]->arrayref }, '@{}' => sub { $_[0]->arrayref },
'fallback' => 1; 'fallback' => 1;
package Slic3r::Point3;
use overload
'@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #,
'fallback' => 1;
sub pp {
my ($self) = @_;
return [ @$self ];
}
package Slic3r::Pointf; package Slic3r::Pointf;
use overload use overload
'@{}' => sub { $_[0]->arrayref }, '@{}' => sub { $_[0]->arrayref },

View file

@ -25,11 +25,11 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <boost/detail/endian.hpp>
#include "stl.h" #include "stl.h"
static void stl_match_neighbors_exact(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_match_neighbors_nearby(stl_file *stl, static void stl_match_neighbors_nearby(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b); stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_record_neighbors(stl_file *stl, static void stl_record_neighbors(stl_file *stl,
@ -43,7 +43,6 @@ static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
void (*match_neighbors)(stl_file *stl, void (*match_neighbors)(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b)); stl_hash_edge *edge_a, stl_hash_edge *edge_b));
static int stl_get_hash_for_edge(int M, stl_hash_edge *edge);
static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_free_edges(stl_file *stl); static void stl_free_edges(stl_file *stl);
static void stl_remove_facet(stl_file *stl, int facet_number); static void stl_remove_facet(stl_file *stl, int facet_number);
@ -82,37 +81,20 @@ stl_check_facets_exact(stl_file *stl) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i]; facet = stl->facet_start[i];
// Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
// When using a memcmp on raw floats, those numbers report to be different. if (facet.vertex[0] == facet.vertex[1] ||
// Unify all +0 and -0 to +0 to make the floats equal under memcmp. facet.vertex[1] == facet.vertex[2] ||
{ facet.vertex[0] == facet.vertex[2]) {
uint32_t *f = (uint32_t*)&facet;
for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats
if (*f == 0x80000000)
// Negative zero, switch to positive zero.
*f = 0;
}
/* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */
if( !memcmp(&facet.vertex[0], &facet.vertex[1],
sizeof(stl_vertex))
|| !memcmp(&facet.vertex[1], &facet.vertex[2],
sizeof(stl_vertex))
|| !memcmp(&facet.vertex[0], &facet.vertex[2],
sizeof(stl_vertex))) {
stl->stats.degenerate_facets += 1; stl->stats.degenerate_facets += 1;
stl_remove_facet(stl, i); stl_remove_facet(stl, i);
i--; -- i;
continue; continue;
} }
for(j = 0; j < 3; j++) { for(j = 0; j < 3; j++) {
edge.facet_number = i; edge.facet_number = i;
edge.which_edge = j; edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j], stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
&facet.vertex[(j + 1) % 3]); insert_hash_edge(stl, edge, stl_record_neighbors);
insert_hash_edge(stl, edge, stl_match_neighbors_exact);
} }
} }
stl_free_edges(stl); stl_free_edges(stl);
@ -131,28 +113,33 @@ stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
if (stl->error) return; if (stl->error) return;
{ {
float diff_x = ABS(a->x - b->x); stl_vertex diff = (*a - *b).cwiseAbs();
float diff_y = ABS(a->y - b->y); float max_diff = std::max(diff(0), std::max(diff(1), diff(2)));
float diff_z = ABS(a->z - b->z); stl->stats.shortest_edge = std::min(max_diff, stl->stats.shortest_edge);
float max_diff = STL_MAX(diff_x, diff_y);
max_diff = STL_MAX(diff_z, max_diff);
stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge);
} }
// Ensure identical vertex ordering of equal edges. // Ensure identical vertex ordering of equal edges.
// This method is numerically robust. // This method is numerically robust.
if ((a->x != b->x) ? if (stl_vertex_lower(*a, *b)) {
(a->x < b->x) :
((a->y != b->y) ?
(a->y < b->y) :
(a->z < b->z))) {
memcpy(&edge->key[0], a, sizeof(stl_vertex));
memcpy(&edge->key[3], b, sizeof(stl_vertex));
} else { } else {
memcpy(&edge->key[0], b, sizeof(stl_vertex)); std::swap(a, b);
memcpy(&edge->key[3], a, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */ edge->which_edge += 3; /* this edge is loaded backwards */
} }
memcpy(&edge->key[0], a->data(), sizeof(stl_vertex));
memcpy(&edge->key[sizeof(stl_vertex)], b->data(), sizeof(stl_vertex));
// Switch negative zeros to positive zeros, so memcmp will consider them to be equal.
for (size_t i = 0; i < 6; ++ i) {
unsigned char *p = edge->key + i * 4;
#ifdef BOOST_LITTLE_ENDIAN
if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80)
// Negative zero, switch to positive zero.
p[3] = 0;
#else /* BOOST_LITTLE_ENDIAN */
if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0)
// Negative zero, switch to positive zero.
p[0] = 0;
#endif /* BOOST_LITTLE_ENDIAN */
}
} }
static void static void
@ -188,21 +175,17 @@ stl_initialize_facet_check_exact(stl_file *stl) {
} }
} }
static void static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
insert_hash_edge(stl_file *stl, stl_hash_edge edge,
void (*match_neighbors)(stl_file *stl, void (*match_neighbors)(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b)) { stl_hash_edge *edge_a, stl_hash_edge *edge_b))
stl_hash_edge *link; {
stl_hash_edge *new_edge;
stl_hash_edge *temp;
int chain_number;
if (stl->error) return; if (stl->error) return;
chain_number = stl_get_hash_for_edge(stl->M, &edge); int chain_number = edge.hash(stl->M);
stl_hash_edge *link = stl->heads[chain_number];
link = stl->heads[chain_number];
stl_hash_edge *new_edge;
stl_hash_edge *temp;
if(link == stl->tail) { if(link == stl->tail) {
/* This list doesn't have any edges currently in it. Add this one. */ /* This list doesn't have any edges currently in it. Add this one. */
new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
@ -252,30 +235,17 @@ insert_hash_edge(stl_file *stl, stl_hash_edge edge,
} }
} }
// Return 1 if the edges are not matched.
static int static inline int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b)
stl_get_hash_for_edge(int M, stl_hash_edge *edge) { {
return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17 // Don't match edges of the same facet
+ edge->key[3] /13 + edge->key[4] / 11 + edge->key[5] / 7 ) % M); return (edge_a->facet_number == edge_b->facet_number) || (*edge_a != *edge_b);
} }
static int void stl_check_facets_nearby(stl_file *stl, float tolerance)
stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) { {
if(edge_a->facet_number == edge_b->facet_number) { if (stl->error)
return 1; /* Don't match edges of the same facet */ return;
} else {
return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT);
}
}
void
stl_check_facets_nearby(stl_file *stl, float tolerance) {
stl_hash_edge edge[3];
stl_facet facet;
int i;
int j;
if (stl->error) return;
if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
&& (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
@ -286,27 +256,19 @@ stl_check_facets_nearby(stl_file *stl, float tolerance) {
stl_initialize_facet_check_nearby(stl); stl_initialize_facet_check_nearby(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) { for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
facet = stl->facet_start[i]; //FIXME is the copy necessary?
// Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. stl_facet facet = stl->facet_start[i];
// When using a memcmp on raw floats, those numbers report to be different. for (int j = 0; j < 3; j++) {
// Unify all +0 and -0 to +0 to make the floats equal under memcmp.
{
uint32_t *f = (uint32_t*)&facet;
for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats
if (*f == 0x80000000)
// Negative zero, switch to positive zero.
*f = 0;
}
for(j = 0; j < 3; j++) {
if(stl->neighbors_start[i].neighbor[j] == -1) { if(stl->neighbors_start[i].neighbor[j] == -1) {
edge[j].facet_number = i; stl_hash_edge edge;
edge[j].which_edge = j; edge.facet_number = i;
if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j], edge.which_edge = j;
if(stl_load_edge_nearby(stl, &edge, &facet.vertex[j],
&facet.vertex[(j + 1) % 3], &facet.vertex[(j + 1) % 3],
tolerance)) { tolerance)) {
/* only insert edges that have different keys */ /* only insert edges that have different keys */
insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby); insert_hash_edge(stl, edge, stl_match_neighbors_nearby);
} }
} }
} }
@ -315,27 +277,17 @@ stl_check_facets_nearby(stl_file *stl, float tolerance) {
stl_free_edges(stl); stl_free_edges(stl);
} }
static int static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b, float tolerance)
stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, {
stl_vertex *a, stl_vertex *b, float tolerance) {
// Index of a grid cell spaced by tolerance. // Index of a grid cell spaced by tolerance.
uint32_t vertex1[3] = { typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i;
(uint32_t)((a->x - stl->stats.min.x) / tolerance), Vec3i vertex1 = (*a / tolerance).cast<int32_t>();
(uint32_t)((a->y - stl->stats.min.y) / tolerance), Vec3i vertex2 = (*b / tolerance).cast<int32_t>();
(uint32_t)((a->z - stl->stats.min.z) / tolerance) static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect");
};
uint32_t vertex2[3] = {
(uint32_t)((b->x - stl->stats.min.x) / tolerance),
(uint32_t)((b->y - stl->stats.min.y) / tolerance),
(uint32_t)((b->z - stl->stats.min.z) / tolerance)
};
if( (vertex1[0] == vertex2[0]) if (vertex1 == vertex2)
&& (vertex1[1] == vertex2[1]) // Both vertices hash to the same value
&& (vertex1[2] == vertex2[2])) {
/* Both vertices hash to the same value */
return 0; return 0;
}
// Ensure identical vertex ordering of edges, which vertices land into equal grid cells. // Ensure identical vertex ordering of edges, which vertices land into equal grid cells.
// This method is numerically robust. // This method is numerically robust.
@ -344,30 +296,27 @@ stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
((vertex1[1] != vertex2[1]) ? ((vertex1[1] != vertex2[1]) ?
(vertex1[1] < vertex2[1]) : (vertex1[1] < vertex2[1]) :
(vertex1[2] < vertex2[2]))) { (vertex1[2] < vertex2[2]))) {
memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); memcpy(&edge->key[0], vertex1.data(), sizeof(stl_vertex));
memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); memcpy(&edge->key[sizeof(stl_vertex)], vertex2.data(), sizeof(stl_vertex));
} else { } else {
memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); memcpy(&edge->key[0], vertex2.data(), sizeof(stl_vertex));
memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); memcpy(&edge->key[sizeof(stl_vertex)], vertex1.data(), sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */ edge->which_edge += 3; /* this edge is loaded backwards */
} }
return 1; return 1;
} }
static void static void stl_free_edges(stl_file *stl)
stl_free_edges(stl_file *stl) { {
int i; if (stl->error)
stl_hash_edge *temp; return;
if (stl->error) return;
if(stl->stats.malloced != stl->stats.freed) { if(stl->stats.malloced != stl->stats.freed) {
for(i = 0; i < stl->M; i++) { for (int i = 0; i < stl->M; i++) {
for(temp = stl->heads[i]; stl->heads[i] != stl->tail; for (stl_hash_edge *temp = stl->heads[i]; stl->heads[i] != stl->tail; temp = stl->heads[i]) {
temp = stl->heads[i]) {
stl->heads[i] = stl->heads[i]->next; stl->heads[i] = stl->heads[i]->next;
free(temp); free(temp);
stl->stats.freed++; ++ stl->stats.freed;
} }
} }
} }
@ -375,8 +324,8 @@ stl_free_edges(stl_file *stl) {
free(stl->tail); free(stl->tail);
} }
static void static void stl_initialize_facet_check_nearby(stl_file *stl)
stl_initialize_facet_check_nearby(stl_file *stl) { {
int i; int i;
if (stl->error) return; if (stl->error) return;
@ -467,16 +416,8 @@ stl_record_neighbors(stl_file *stl,
} }
} }
static void static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b)
stl_match_neighbors_exact(stl_file *stl, {
stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
if (stl->error) return;
stl_record_neighbors(stl, edge_a, edge_b);
}
static void
stl_match_neighbors_nearby(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
int facet1; int facet1;
int facet2; int facet2;
int vertex1; int vertex1;
@ -517,9 +458,7 @@ stl_match_neighbors_nearby(stl_file *stl,
} }
static void static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, stl_vertex new_vertex) {
stl_change_vertices(stl_file *stl, int facet_num, int vnot,
stl_vertex new_vertex) {
int first_facet; int first_facet;
int direction; int direction;
int next_edge; int next_edge;
@ -551,30 +490,30 @@ stl_change_vertices(stl_file *stl, int facet_num, int vnot,
} }
} }
#if 0 #if 0
if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) &&
stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) &&
stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2))
printf("Changing vertex %f,%f,%f: Same !!!\r\n", printf("Changing vertex %f,%f,%f: Same !!!\r\n",
new_vertex.x, new_vertex.y, new_vertex.z); new_vertex(0), new_vertex(1), new_vertex(2));
else { else {
if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0))
printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
stl->facet_start[facet_num].vertex[pivot_vertex].x, stl->facet_start[facet_num].vertex[pivot_vertex](0),
*reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].x), *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](0)),
new_vertex.x, new_vertex(0),
*reinterpret_cast<const int*>(&new_vertex.x)); *reinterpret_cast<const int*>(&new_vertex(0)));
if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1))
printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
stl->facet_start[facet_num].vertex[pivot_vertex].y, stl->facet_start[facet_num].vertex[pivot_vertex](1),
*reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].y), *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](1)),
new_vertex.y, new_vertex(1),
*reinterpret_cast<const int*>(&new_vertex.y)); *reinterpret_cast<const int*>(&new_vertex(1)));
if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2))
printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n",
stl->facet_start[facet_num].vertex[pivot_vertex].z, stl->facet_start[facet_num].vertex[pivot_vertex](2),
*reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex].z), *reinterpret_cast<const int*>(&stl->facet_start[facet_num].vertex[pivot_vertex](2)),
new_vertex.z, new_vertex(2),
*reinterpret_cast<const int*>(&new_vertex.z)); *reinterpret_cast<const int*>(&new_vertex(2)));
} }
#endif #endif
stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex;
@ -595,7 +534,6 @@ Try using a smaller tolerance or don't do a nearby check\n");
} }
} }
static void static void
stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
stl_hash_edge *edge_b, int *facet1, int *vertex1, stl_hash_edge *edge_b, int *facet1, int *vertex1,
@ -622,11 +560,10 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
v1b = (edge_b->which_edge + 1) % 3; v1b = (edge_b->which_edge + 1) % 3;
} }
/* Of the first pair, which vertex, if any, should be changed */ // Of the first pair, which vertex, if any, should be changed
if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a], if(stl->facet_start[edge_a->facet_number].vertex[v1a] ==
&stl->facet_start[edge_b->facet_number].vertex[v1b], stl->facet_start[edge_b->facet_number].vertex[v1b]) {
sizeof(stl_vertex))) { // These facets are already equal. No need to change.
/* These facets are already equal. No need to change. */
*facet1 = -1; *facet1 = -1;
} else { } else {
if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1)
@ -644,10 +581,9 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
} }
/* Of the second pair, which vertex, if any, should be changed */ /* Of the second pair, which vertex, if any, should be changed */
if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a], if(stl->facet_start[edge_a->facet_number].vertex[v2a] ==
&stl->facet_start[edge_b->facet_number].vertex[v2b], stl->facet_start[edge_b->facet_number].vertex[v2b]) {
sizeof(stl_vertex))) { // These facets are already equal. No need to change.
/* These facets are already equal. No need to change. */
*facet2 = -1; *facet2 = -1;
} else { } else {
if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1)
@ -718,40 +654,35 @@ in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n",
} }
} }
void void stl_remove_unconnected_facets(stl_file *stl)
stl_remove_unconnected_facets(stl_file *stl) { {
/* A couple of things need to be done here. One is to remove any */ /* A couple of things need to be done here. One is to remove any */
/* completely unconnected facets (0 edges connected) since these are */ /* completely unconnected facets (0 edges connected) since these are */
/* useless and could be completely wrong. The second thing that needs to */ /* useless and could be completely wrong. The second thing that needs to */
/* be done is to remove any degenerate facets that were created during */ /* be done is to remove any degenerate facets that were created during */
/* stl_check_facets_nearby(). */ /* stl_check_facets_nearby(). */
if (stl->error)
return;
int i; // remove degenerate facets
for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
if (stl->error) return; if(stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] ||
stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] ||
/* remove degenerate facets */ stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) {
for(i = 0; i < stl->stats.number_of_facets; i++) {
if( !memcmp(&stl->facet_start[i].vertex[0],
&stl->facet_start[i].vertex[1], sizeof(stl_vertex))
|| !memcmp(&stl->facet_start[i].vertex[1],
&stl->facet_start[i].vertex[2], sizeof(stl_vertex))
|| !memcmp(&stl->facet_start[i].vertex[0],
&stl->facet_start[i].vertex[2], sizeof(stl_vertex))) {
stl_remove_degenerate(stl, i); stl_remove_degenerate(stl, i);
i--; i--;
} }
} }
if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) {
/* remove completely unconnected facets */ // remove completely unconnected facets
for(i = 0; i < stl->stats.number_of_facets; i++) { for (int i = 0; i < stl->stats.number_of_facets; i++) {
if( (stl->neighbors_start[i].neighbor[0] == -1) if (stl->neighbors_start[i].neighbor[0] == -1 &&
&& (stl->neighbors_start[i].neighbor[1] == -1) stl->neighbors_start[i].neighbor[1] == -1 &&
&& (stl->neighbors_start[i].neighbor[2] == -1)) { stl->neighbors_start[i].neighbor[2] == -1) {
/* This facet is completely unconnected. Remove it. */ // This facet is completely unconnected. Remove it.
stl_remove_facet(stl, i); stl_remove_facet(stl, i);
i--; -- i;
} }
} }
} }
@ -771,30 +702,24 @@ stl_remove_degenerate(stl_file *stl, int facet) {
if (stl->error) return; if (stl->error) return;
if( !memcmp(&stl->facet_start[facet].vertex[0], if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1] &&
&stl->facet_start[facet].vertex[1], sizeof(stl_vertex)) stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
&& !memcmp(&stl->facet_start[facet].vertex[1],
&stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
/* all 3 vertices are equal. Just remove the facet. I don't think*/ /* all 3 vertices are equal. Just remove the facet. I don't think*/
/* this is really possible, but just in case... */ /* this is really possible, but just in case... */
printf("removing a facet in stl_remove_degenerate\n"); printf("removing a facet in stl_remove_degenerate\n");
stl_remove_facet(stl, facet); stl_remove_facet(stl, facet);
return; return;
} }
if(!memcmp(&stl->facet_start[facet].vertex[0], if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) {
&stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) {
edge1 = 1; edge1 = 1;
edge2 = 2; edge2 = 2;
edge3 = 0; edge3 = 0;
} else if(!memcmp(&stl->facet_start[facet].vertex[1], } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) {
&stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
edge1 = 0; edge1 = 0;
edge2 = 2; edge2 = 2;
edge3 = 1; edge3 = 1;
} else if(!memcmp(&stl->facet_start[facet].vertex[2], } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) {
&stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) {
edge1 = 0; edge1 = 0;
edge2 = 1; edge2 = 1;
edge3 = 2; edge3 = 2;
@ -883,7 +808,7 @@ stl_fill_holes(stl_file *stl) {
stl_load_edge_exact(stl, &edge, &facet.vertex[j], stl_load_edge_exact(stl, &edge, &facet.vertex[j],
&facet.vertex[(j + 1) % 3]); &facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_match_neighbors_exact); insert_hash_edge(stl, edge, stl_record_neighbors);
} }
} }
@ -939,7 +864,7 @@ stl_fill_holes(stl_file *stl) {
stl_load_edge_exact(stl, &edge, &new_facet.vertex[k], stl_load_edge_exact(stl, &edge, &new_facet.vertex[k],
&new_facet.vertex[(k + 1) % 3]); &new_facet.vertex[(k + 1) % 3]);
insert_hash_edge(stl, edge, stl_match_neighbors_exact); insert_hash_edge(stl, edge, stl_record_neighbors);
} }
break; break;
} else { } else {
@ -977,9 +902,7 @@ stl_add_facet(stl_file *stl, stl_facet *new_facet) {
stl->facet_start[stl->stats.number_of_facets] = *new_facet; stl->facet_start[stl->stats.number_of_facets] = *new_facet;
/* note that the normal vector is not set here, just initialized to 0 */ /* note that the normal vector is not set here, just initialized to 0 */
stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0; stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero();
stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0;
stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0;
stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1; stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1;
stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1; stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1;

View file

@ -27,12 +27,6 @@
#include "stl.h" #include "stl.h"
static void stl_reverse_vector(float v[]) {
v[0] *= -1;
v[1] *= -1;
v[2] *= -1;
}
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag);
static void static void
@ -228,102 +222,52 @@ static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_
/* Returns 2 if the normal is not within tolerance and backwards */ /* Returns 2 if the normal is not within tolerance and backwards */
/* Returns 4 if the status is unknown. */ /* Returns 4 if the status is unknown. */
float normal[3];
float test_norm[3];
stl_facet *facet; stl_facet *facet;
facet = &stl->facet_start[facet_num]; facet = &stl->facet_start[facet_num];
stl_normal normal;
stl_calculate_normal(normal, facet); stl_calculate_normal(normal, facet);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl_normal normal_dif = (normal - facet->normal).cwiseAbs();
if( (ABS(normal[0] - facet->normal.x) < 0.001) const float eps = 0.001f;
&& (ABS(normal[1] - facet->normal.y) < 0.001) if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
&& (ABS(normal[2] - facet->normal.z) < 0.001)) {
/* It is not really necessary to change the values here */ /* It is not really necessary to change the values here */
/* but just for consistency, I will. */ /* but just for consistency, I will. */
facet->normal.x = normal[0]; facet->normal = normal;
facet->normal.y = normal[1];
facet->normal.z = normal[2];
return 0; return 0;
} }
test_norm[0] = facet->normal.x; stl_normal test_norm = facet->normal;
test_norm[1] = facet->normal.y;
test_norm[2] = facet->normal.z;
stl_normalize_vector(test_norm); stl_normalize_vector(test_norm);
if( (ABS(normal[0] - test_norm[0]) < 0.001) normal_dif = (normal - test_norm).cwiseAbs();
&& (ABS(normal[1] - test_norm[1]) < 0.001) if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
&& (ABS(normal[2] - test_norm[2]) < 0.001)) {
if(normal_fix_flag) { if(normal_fix_flag) {
facet->normal.x = normal[0]; facet->normal = normal;
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1; stl->stats.normals_fixed += 1;
} }
return 1; return 1;
} }
stl_reverse_vector(test_norm); test_norm *= -1.f;
if( (ABS(normal[0] - test_norm[0]) < 0.001) normal_dif = (normal - test_norm).cwiseAbs();
&& (ABS(normal[1] - test_norm[1]) < 0.001) if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
&& (ABS(normal[2] - test_norm[2]) < 0.001)) { // Facet is backwards.
/* Facet is backwards. */
if(normal_fix_flag) { if(normal_fix_flag) {
facet->normal.x = normal[0]; facet->normal = normal;
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1; stl->stats.normals_fixed += 1;
} }
return 2; return 2;
} }
if(normal_fix_flag) { if(normal_fix_flag) {
facet->normal.x = normal[0]; facet->normal = normal;
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1; stl->stats.normals_fixed += 1;
} }
return 4; return 4;
} }
void stl_calculate_normal(float normal[], stl_facet *facet) { void stl_fix_normal_values(stl_file *stl) {
float v1[3] = {
facet->vertex[1].x - facet->vertex[0].x,
facet->vertex[1].y - facet->vertex[0].y,
facet->vertex[1].z - facet->vertex[0].z
};
float v2[3] = {
facet->vertex[2].x - facet->vertex[0].x,
facet->vertex[2].y - facet->vertex[0].y,
facet->vertex[2].z - facet->vertex[0].z
};
normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]);
normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]);
normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]);
}
void stl_normalize_vector(float v[]) {
double length;
double factor;
float min_normal_length;
length = sqrt((double)v[0] * (double)v[0] + (double)v[1] * (double)v[1] + (double)v[2] * (double)v[2]);
min_normal_length = 0.000000000001;
if(length < min_normal_length) {
v[0] = 0.0;
v[1] = 0.0;
v[2] = 0.0;
return;
}
factor = 1.0 / length;
v[0] *= factor;
v[1] *= factor;
v[2] *= factor;
}
void
stl_fix_normal_values(stl_file *stl) {
int i; int i;
if (stl->error) return; if (stl->error) return;
@ -333,20 +277,16 @@ stl_fix_normal_values(stl_file *stl) {
} }
} }
void void stl_reverse_all_facets(stl_file *stl)
stl_reverse_all_facets(stl_file *stl) { {
int i; if (stl->error)
float normal[3]; return;
if (stl->error) return; stl_normal normal;
for(int i = 0; i < stl->stats.number_of_facets; i++) {
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl_reverse_facet(stl, i); stl_reverse_facet(stl, i);
stl_calculate_normal(normal, &stl->facet_start[i]); stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl->facet_start[i].normal.x = normal[0]; stl->facet_start[i].normal = normal;
stl->facet_start[i].normal.y = normal[1];
stl->facet_start[i].normal.z = normal[2];
} }
} }

View file

@ -169,7 +169,7 @@ stl_write_off(stl_file *stl, char *file) {
for(i = 0; i < stl->stats.shared_vertices; i++) { for(i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "\t%f %f %f\n", fprintf(fp, "\t%f %f %f\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
} }
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0],
@ -216,10 +216,10 @@ stl_write_vrml(stl_file *stl, char *file) {
for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { for(i = 0; i < (stl->stats.shared_vertices - 1); i++) {
fprintf(fp, "\t\t\t\t%f %f %f,\n", fprintf(fp, "\t\t\t\t%f %f %f,\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
} }
fprintf(fp, "\t\t\t\t%f %f %f]\n", fprintf(fp, "\t\t\t\t%f %f %f]\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
fprintf(fp, "\t\t}\n"); fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\t\tcoordIndex [\n"); fprintf(fp, "\t\t\tcoordIndex [\n");
@ -254,7 +254,7 @@ void stl_write_obj (stl_file *stl, char *file) {
} }
for (i = 0; i < stl->stats.shared_vertices; i++) { for (i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
} }
for (i = 0; i < stl->stats.number_of_facets; i++) { for (i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);

View file

@ -27,9 +27,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#define STL_MAX(A,B) ((A)>(B)? (A):(B)) #include <Eigen/Geometry>
#define STL_MIN(A,B) ((A)<(B)? (A):(B))
#define ABS(X) ((X) < 0 ? -(X) : (X))
// Size of the binary STL header, free form. // Size of the binary STL header, free form.
#define LABEL_SIZE 80 #define LABEL_SIZE 80
@ -39,31 +37,16 @@
#define HEADER_SIZE 84 #define HEADER_SIZE 84
#define STL_MIN_FILE_SIZE 284 #define STL_MIN_FILE_SIZE 284
#define ASCII_LINES_PER_FACET 7 #define ASCII_LINES_PER_FACET 7
// Comparing an edge by memcmp, 2x3x4 bytes = 24
#define SIZEOF_EDGE_SORT 24
typedef struct {
float x;
float y;
float z;
} stl_vertex;
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_vertex;
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal;
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
typedef struct {
float x;
float y;
float z;
} stl_normal;
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
typedef char stl_extra[2];
typedef struct { typedef struct {
stl_normal normal; stl_normal normal;
stl_vertex vertex[3]; stl_vertex vertex[3];
stl_extra extra; char extra[2];
} stl_facet; } stl_facet;
#define SIZEOF_STL_FACET 50 #define SIZEOF_STL_FACET 50
@ -81,8 +64,12 @@ typedef struct {
} stl_edge; } stl_edge;
typedef struct stl_hash_edge { typedef struct stl_hash_edge {
// Key of a hash edge: 2x binary copy of a floating point vertex. // Key of a hash edge: sorted vertices of the edge.
uint32_t key[6]; unsigned char key[2 * sizeof(stl_vertex)];
// Compare two keys.
bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; }
bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); }
int hash(int M) const { return ((key[0] / 23 + key[1] / 19 + key[2] / 17 + key[3] /13 + key[4] / 11 + key[5] / 7 ) % M); }
// Index of a facet owning this edge. // Index of a facet owning this edge.
int facet_number; int facet_number;
// Index of this edge inside the facet with an index of facet_number. // Index of this edge inside the facet with an index of facet_number.
@ -91,8 +78,6 @@ typedef struct stl_hash_edge {
struct stl_hash_edge *next; struct stl_hash_edge *next;
} stl_hash_edge; } stl_hash_edge;
static_assert(offsetof(stl_hash_edge, facet_number) == SIZEOF_EDGE_SORT, "size of stl_hash_edge.key incorrect");
typedef struct { typedef struct {
// Index of a neighbor facet. // Index of a neighbor facet.
int neighbor[3]; int neighbor[3];
@ -179,8 +164,8 @@ extern void stl_fix_normal_values(stl_file *stl);
extern void stl_reverse_all_facets(stl_file *stl); extern void stl_reverse_all_facets(stl_file *stl);
extern void stl_translate(stl_file *stl, float x, float y, float z); extern void stl_translate(stl_file *stl, float x, float y, float z);
extern void stl_translate_relative(stl_file *stl, float x, float y, float z); extern void stl_translate_relative(stl_file *stl, float x, float y, float z);
extern void stl_scale_versor(stl_file *stl, float versor[3]); extern void stl_scale_versor(stl_file *stl, const stl_vertex &versor);
extern void stl_scale(stl_file *stl, float factor); inline void stl_scale(stl_file *stl, float factor) { stl_scale_versor(stl, stl_vertex(factor, factor, factor)); }
extern void stl_rotate_x(stl_file *stl, float angle); extern void stl_rotate_x(stl_file *stl, float angle);
extern void stl_rotate_y(stl_file *stl, float angle); extern void stl_rotate_y(stl_file *stl, float angle);
extern void stl_rotate_z(stl_file *stl, float angle); extern void stl_rotate_z(stl_file *stl, float angle);
@ -195,8 +180,20 @@ extern void stl_write_obj(stl_file *stl, char *file);
extern void stl_write_off(stl_file *stl, char *file); extern void stl_write_off(stl_file *stl, char *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label); extern void stl_write_dxf(stl_file *stl, char *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file); extern void stl_write_vrml(stl_file *stl, char *file);
extern void stl_calculate_normal(float normal[], stl_facet *facet); inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
extern void stl_normalize_vector(float v[]); normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
}
inline void stl_normalize_vector(stl_normal &normal) {
double length = normal.cast<double>().norm();
if (length < 0.000000000001)
normal = stl_normal::Zero();
else
normal *= (1.0 / length);
}
inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
return (a(0) != b(0)) ? (a(0) < b(0)) :
((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
}
extern void stl_calculate_volume(stl_file *stl); extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
@ -204,8 +201,8 @@ extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int toler
extern void stl_initialize(stl_file *stl); extern void stl_initialize(stl_file *stl);
extern void stl_count_facets(stl_file *stl, const char *file); extern void stl_count_facets(stl_file *stl, const char *file);
extern void stl_allocate(stl_file *stl); extern void stl_allocate(stl_file *stl);
extern void stl_read(stl_file *stl, int first_facet, int first); extern void stl_read(stl_file *stl, int first_facet, bool first);
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first);
extern void stl_reallocate(stl_file *stl); extern void stl_reallocate(stl_file *stl);
extern void stl_add_facet(stl_file *stl, stl_facet *new_facet); extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
extern void stl_get_size(stl_file *stl); extern void stl_get_size(stl_file *stl);

View file

@ -44,9 +44,9 @@ stl_print_edges(stl_file *stl, FILE *file) {
for(i = 0; i < edges_allocated; i++) { for(i = 0; i < edges_allocated; i++) {
fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n", fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
stl->edge_start[i].facet_number, stl->edge_start[i].facet_number,
stl->edge_start[i].p1.x, stl->edge_start[i].p1.y, stl->edge_start[i].p1(0), stl->edge_start[i].p1(1),
stl->edge_start[i].p1.z, stl->edge_start[i].p2.x, stl->edge_start[i].p1(2), stl->edge_start[i].p2(0),
stl->edge_start[i].p2.y, stl->edge_start[i].p2.z); stl->edge_start[i].p2(1), stl->edge_start[i].p2(2));
} }
} }
@ -75,11 +75,11 @@ File type : ASCII STL file\n");
Header : %s\n", stl->stats.header); Header : %s\n", stl->stats.header);
fprintf(file, "============== Size ==============\n"); fprintf(file, "============== Size ==============\n");
fprintf(file, "Min X = % f, Max X = % f\n", fprintf(file, "Min X = % f, Max X = % f\n",
stl->stats.min.x, stl->stats.max.x); stl->stats.min(0), stl->stats.max(0));
fprintf(file, "Min Y = % f, Max Y = % f\n", fprintf(file, "Min Y = % f, Max Y = % f\n",
stl->stats.min.y, stl->stats.max.y); stl->stats.min(1), stl->stats.max(1));
fprintf(file, "Min Z = % f, Max Z = % f\n", fprintf(file, "Min Z = % f, Max Z = % f\n",
stl->stats.min.z, stl->stats.max.z); stl->stats.min(2), stl->stats.max(2));
fprintf(file, "\ fprintf(file, "\
========= Facet Status ========== Original ============ Final ====\n"); ========= Facet Status ========== Original ============ Final ====\n");
@ -149,18 +149,18 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, " facet normal % .8E % .8E % .8E\n", fprintf(fp, " facet normal % .8E % .8E % .8E\n",
stl->facet_start[i].normal.x, stl->facet_start[i].normal.y, stl->facet_start[i].normal(0), stl->facet_start[i].normal(1),
stl->facet_start[i].normal.z); stl->facet_start[i].normal(2));
fprintf(fp, " outer loop\n"); fprintf(fp, " outer loop\n");
fprintf(fp, " vertex % .8E % .8E % .8E\n", fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0].z); stl->facet_start[i].vertex[0](2));
fprintf(fp, " vertex % .8E % .8E % .8E\n", fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1].z); stl->facet_start[i].vertex[1](2));
fprintf(fp, " vertex % .8E % .8E % .8E\n", fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2].z); stl->facet_start[i].vertex[2](2));
fprintf(fp, " endloop\n"); fprintf(fp, " endloop\n");
fprintf(fp, " endfacet\n"); fprintf(fp, " endfacet\n");
} }
@ -264,9 +264,9 @@ void
stl_write_vertex(stl_file *stl, int facet, int vertex) { stl_write_vertex(stl_file *stl, int facet, int vertex) {
if (stl->error) return; if (stl->error) return;
printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
stl->facet_start[facet].vertex[vertex].x, stl->facet_start[facet].vertex[vertex](0),
stl->facet_start[facet].vertex[vertex].y, stl->facet_start[facet].vertex[vertex](1),
stl->facet_start[facet].vertex[vertex].z); stl->facet_start[facet].vertex[vertex](2));
} }
void void
@ -309,10 +309,10 @@ stl_write_quad_object(stl_file *stl, char *file) {
int i; int i;
int j; int j;
char *error_msg; char *error_msg;
stl_vertex connect_color; stl_vertex connect_color = stl_vertex::Zero();
stl_vertex uncon_1_color; stl_vertex uncon_1_color = stl_vertex::Zero();
stl_vertex uncon_2_color; stl_vertex uncon_2_color = stl_vertex::Zero();
stl_vertex uncon_3_color; stl_vertex uncon_3_color = stl_vertex::Zero();
stl_vertex color; stl_vertex color;
if (stl->error) return; if (stl->error) return;
@ -330,19 +330,6 @@ stl_write_quad_object(stl_file *stl, char *file) {
return; return;
} }
connect_color.x = 0.0;
connect_color.y = 0.0;
connect_color.z = 1.0;
uncon_1_color.x = 0.0;
uncon_1_color.y = 1.0;
uncon_1_color.z = 0.0;
uncon_2_color.x = 1.0;
uncon_2_color.y = 1.0;
uncon_2_color.z = 1.0;
uncon_3_color.x = 1.0;
uncon_3_color.y = 0.0;
uncon_3_color.z = 0.0;
fprintf(fp, "CQUAD\n"); fprintf(fp, "CQUAD\n");
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
j = ((stl->neighbors_start[i].neighbor[0] == -1) + j = ((stl->neighbors_start[i].neighbor[0] == -1) +
@ -358,21 +345,21 @@ stl_write_quad_object(stl_file *stl, char *file) {
color = uncon_3_color; color = uncon_3_color;
} }
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0](0),
stl->facet_start[i].vertex[0].y, stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0].z, color.x, color.y, color.z); stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1](0),
stl->facet_start[i].vertex[1].y, stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1].z, color.x, color.y, color.z); stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2](0),
stl->facet_start[i].vertex[2].y, stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2](0),
stl->facet_start[i].vertex[2].y, stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
} }
fclose(fp); fclose(fp);
} }
@ -409,17 +396,17 @@ stl_write_dxf(stl_file *stl, char *file, char *label) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "0\n3DFACE\n8\n0\n"); fprintf(fp, "0\n3DFACE\n8\n0\n");
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n",
stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
stl->facet_start[i].vertex[0].z); stl->facet_start[i].vertex[0](2));
fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n",
stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
stl->facet_start[i].vertex[1].z); stl->facet_start[i].vertex[1](2));
fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2].z); stl->facet_start[i].vertex[2](2));
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
stl->facet_start[i].vertex[2].z); stl->facet_start[i].vertex[2](2));
} }
fprintf(fp, "0\nENDSEC\n0\nEOF\n"); fprintf(fp, "0\nENDSEC\n0\nEOF\n");

View file

@ -40,7 +40,7 @@ stl_open(stl_file *stl, const char *file) {
stl_initialize(stl); stl_initialize(stl);
stl_count_facets(stl, file); stl_count_facets(stl, file);
stl_allocate(stl); stl_allocate(stl);
stl_read(stl, 0, 1); stl_read(stl, 0, true);
if (!stl->error) fclose(stl->fp); if (!stl->error) fclose(stl->fp);
} }
@ -227,7 +227,7 @@ stl_open_merge(stl_file *stl, char *file_to_merge) {
Start at num_facets_so_far, the index to the first unused facet. Also say Start at num_facets_so_far, the index to the first unused facet. Also say
that this isn't our first time so we should augment stats like min and max that this isn't our first time so we should augment stats like min and max
instead of erasing them. */ instead of erasing them. */
stl_read(stl, num_facets_so_far, 0); stl_read(stl, num_facets_so_far, false);
/* Restore the stl information we overwrote (for stl_read) so that it still accurately /* Restore the stl information we overwrote (for stl_read) so that it still accurately
reflects the subject part: */ reflects the subject part: */
@ -255,8 +255,7 @@ stl_reallocate(stl_file *stl) {
/* Reads the contents of the file pointed to by stl->fp into the stl structure, /* Reads the contents of the file pointed to by stl->fp into the stl structure,
starting at facet first_facet. The second argument says if it's our first starting at facet first_facet. The second argument says if it's our first
time running this for the stl and therefore we should reset our max and min stats. */ time running this for the stl and therefore we should reset our max and min stats. */
void void stl_read(stl_file *stl, int first_facet, bool first) {
stl_read(stl_file *stl, int first_facet, int first) {
stl_facet facet; stl_facet facet;
int i; int i;
@ -294,11 +293,11 @@ stl_read(stl_file *stl, int first_facet, int first) {
assert(res_normal == 3); assert(res_normal == 3);
int res_outer_loop = fscanf(stl->fp, " outer loop"); int res_outer_loop = fscanf(stl->fp, " outer loop");
assert(res_outer_loop == 0); assert(res_outer_loop == 0);
int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3); assert(res_vertex1 == 3);
int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3); assert(res_vertex2 == 3);
int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3); assert(res_vertex3 == 3);
int res_endloop = fscanf(stl->fp, " endloop"); int res_endloop = fscanf(stl->fp, " endloop");
assert(res_endloop == 0); assert(res_endloop == 0);
@ -311,9 +310,9 @@ stl_read(stl_file *stl, int first_facet, int first) {
} }
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored? // Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it. // Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal)); memset(&facet.normal, 0, sizeof(facet.normal));
@ -326,104 +325,45 @@ stl_read(stl_file *stl, int first_facet, int first) {
// It may be worth to round these numbers to zero during loading to reduce the number of errors reported // It may be worth to round these numbers to zero during loading to reduce the number of errors reported
// during the STL import. // during the STL import.
for (size_t j = 0; j < 3; ++ j) { for (size_t j = 0; j < 3; ++ j) {
if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
} }
#endif #endif
#if 1
{
// Positive and negative zeros are possible in the floats, which are considered equal by the FP unit.
// When using a memcmp on raw floats, those numbers report to be different.
// Unify all +0 and -0 to +0 to make the floats equal under memcmp.
uint32_t *f = (uint32_t*)&facet;
for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats
if (*f == 0x80000000)
// Negative zero, switch to positive zero.
*f = 0;
}
#else
{
// Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision
// than the rest of the vertices. Round them to zero.
float *f = (float*)&facet;
for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats
if (*f > -1e-12f && *f < 1e-12f)
// Negative zero, switch to positive zero.
*f = 0;
}
#endif
/* Write the facet into memory. */ /* Write the facet into memory. */
memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); stl->facet_start[i] = facet;
stl_facet_stats(stl, facet, first); stl_facet_stats(stl, facet, first);
first = 0;
} }
stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; stl->stats.size = stl->stats.max - stl->stats.min;
stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; stl->stats.bounding_diameter = stl->stats.size.norm();
stl->stats.size.z = stl->stats.max.z - stl->stats.min.z;
stl->stats.bounding_diameter = sqrt(
stl->stats.size.x * stl->stats.size.x +
stl->stats.size.y * stl->stats.size.y +
stl->stats.size.z * stl->stats.size.z
);
} }
void void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
stl_facet_stats(stl_file *stl, stl_facet facet, int first) { {
float diff_x; if (stl->error)
float diff_y; return;
float diff_z;
float max_diff;
if (stl->error) return; // While we are going through all of the facets, let's find the
// maximum and minimum values for x, y, and z
/* while we are going through all of the facets, let's find the */
/* maximum and minimum values for x, y, and z */
/* Initialize the max and min values the first time through*/
if (first) { if (first) {
stl->stats.max.x = facet.vertex[0].x; // Initialize the max and min values the first time through
stl->stats.min.x = facet.vertex[0].x; stl->stats.min = facet.vertex[0];
stl->stats.max.y = facet.vertex[0].y; stl->stats.max = facet.vertex[0];
stl->stats.min.y = facet.vertex[0].y; stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs();
stl->stats.max.z = facet.vertex[0].z; stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
stl->stats.min.z = facet.vertex[0].z; first = false;
diff_x = ABS(facet.vertex[0].x - facet.vertex[1].x);
diff_y = ABS(facet.vertex[0].y - facet.vertex[1].y);
diff_z = ABS(facet.vertex[0].z - facet.vertex[1].z);
max_diff = STL_MAX(diff_x, diff_y);
max_diff = STL_MAX(diff_z, max_diff);
stl->stats.shortest_edge = max_diff;
first = 0;
} }
/* now find the max and min values */ // Now find the max and min values.
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[0].x); for (size_t i = 0; i < 3; ++ i) {
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[0].x); stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[0].y); stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[0].y); }
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[0].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[0].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[1].x);
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[1].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[1].y);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[1].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[1].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[1].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[2].x);
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[2].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[2].y);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[2].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[2].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[2].z);
} }
void void

View file

@ -62,7 +62,7 @@ stl_verify_neighbors(stl_file *stl) {
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
} }
if(memcmp(&edge_a, &edge_b, SIZEOF_EDGE_SORT) != 0) { if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
/* These edges should match but they don't. Print results. */ /* These edges should match but they don't. Print results. */
printf("edge %d of facet %d doesn't match edge %d of facet %d\n", printf("edge %d of facet %d doesn't match edge %d of facet %d\n",
j, i, vnot + 1, neighbor); j, i, vnot + 1, neighbor);
@ -73,114 +73,67 @@ stl_verify_neighbors(stl_file *stl) {
} }
} }
void void stl_translate(stl_file *stl, float x, float y, float z)
stl_translate(stl_file *stl, float x, float y, float z) { {
int i; if (stl->error)
int j; return;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x -= (stl->stats.min.x - x);
stl->facet_start[i].vertex[j].y -= (stl->stats.min.y - y);
stl->facet_start[i].vertex[j].z -= (stl->stats.min.z - z);
}
}
stl->stats.max.x -= (stl->stats.min.x - x);
stl->stats.max.y -= (stl->stats.min.y - y);
stl->stats.max.z -= (stl->stats.min.z - z);
stl->stats.min.x = x;
stl->stats.min.y = y;
stl->stats.min.z = z;
stl_vertex new_min(x, y, z);
stl_vertex shift = new_min - stl->stats.min;
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j] += shift;
stl->stats.min = new_min;
stl->stats.max += shift;
stl_invalidate_shared_vertices(stl); stl_invalidate_shared_vertices(stl);
} }
/* Translates the stl by x,y,z, relatively from wherever it is currently */ /* Translates the stl by x,y,z, relatively from wherever it is currently */
void void stl_translate_relative(stl_file *stl, float x, float y, float z)
stl_translate_relative(stl_file *stl, float x, float y, float z) { {
int i; if (stl->error)
int j; return;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x += x;
stl->facet_start[i].vertex[j].y += y;
stl->facet_start[i].vertex[j].z += z;
}
}
stl->stats.min.x += x;
stl->stats.min.y += y;
stl->stats.min.z += z;
stl->stats.max.x += x;
stl->stats.max.y += y;
stl->stats.max.z += z;
stl_vertex shift(x, y, z);
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j] += shift;
stl->stats.min += shift;
stl->stats.max += shift;
stl_invalidate_shared_vertices(stl); stl_invalidate_shared_vertices(stl);
} }
void void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
stl_scale_versor(stl_file *stl, float versor[3]) { {
int i; if (stl->error)
int j; return;
if (stl->error) return;
/* scale extents */
stl->stats.min.x *= versor[0];
stl->stats.min.y *= versor[1];
stl->stats.min.z *= versor[2];
stl->stats.max.x *= versor[0];
stl->stats.max.y *= versor[1];
stl->stats.max.z *= versor[2];
/* scale size */
stl->stats.size.x *= versor[0];
stl->stats.size.y *= versor[1];
stl->stats.size.z *= versor[2];
/* scale volume */
if (stl->stats.volume > 0.0) {
stl->stats.volume *= (versor[0] * versor[1] * versor[2]);
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x *= versor[0];
stl->facet_start[i].vertex[j].y *= versor[1];
stl->facet_start[i].vertex[j].z *= versor[2];
}
}
// Scale extents.
auto s = versor.array();
stl->stats.min.array() *= s;
stl->stats.max.array() *= s;
// Scale size.
stl->stats.size.array() *= s;
// Scale volume.
if (stl->stats.volume > 0.0)
stl->stats.volume *= versor(0) * versor(1) * versor(2);
// Scale the mesh.
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
for (int j = 0; j < 3; ++ j)
stl->facet_start[i].vertex[j].array() *= s;
stl_invalidate_shared_vertices(stl); stl_invalidate_shared_vertices(stl);
} }
void static void calculate_normals(stl_file *stl)
stl_scale(stl_file *stl, float factor) { {
float versor[3]; if (stl->error)
return;
if (stl->error) return;
versor[0] = factor;
versor[1] = factor;
versor[2] = factor;
stl_scale_versor(stl, versor);
}
static void calculate_normals(stl_file *stl) {
float normal[3];
if (stl->error) return;
stl_normal normal;
for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
stl_calculate_normal(normal, &stl->facet_start[i]); stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl->facet_start[i].normal.x = normal[0]; stl->facet_start[i].normal = normal;
stl->facet_start[i].normal.y = normal[1];
stl->facet_start[i].normal.z = normal[2];
} }
} }
@ -193,9 +146,9 @@ void stl_transform(stl_file *stl, float *trafo3x4) {
for (i_vertex = 0; i_vertex < 3; ++ i_vertex) { for (i_vertex = 0; i_vertex < 3; ++ i_vertex) {
stl_vertex &v_dst = vertices[i_vertex]; stl_vertex &v_dst = vertices[i_vertex];
stl_vertex v_src = v_dst; stl_vertex v_src = v_dst;
v_dst.x = trafo3x4[0] * v_src.x + trafo3x4[1] * v_src.y + trafo3x4[2] * v_src.z + trafo3x4[3]; v_dst(0) = trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3];
v_dst.y = trafo3x4[4] * v_src.x + trafo3x4[5] * v_src.y + trafo3x4[6] * v_src.z + trafo3x4[7]; v_dst(1) = trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7];
v_dst.z = trafo3x4[8] * v_src.x + trafo3x4[9] * v_src.y + trafo3x4[10] * v_src.z + trafo3x4[11]; v_dst(2) = trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11];
} }
} }
stl_get_size(stl); stl_get_size(stl);
@ -214,8 +167,8 @@ stl_rotate_x(stl_file *stl, float angle) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) { for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].y, stl_rotate(&stl->facet_start[i].vertex[j](1),
&stl->facet_start[i].vertex[j].z, c, s); &stl->facet_start[i].vertex[j](2), c, s);
} }
} }
stl_get_size(stl); stl_get_size(stl);
@ -234,8 +187,8 @@ stl_rotate_y(stl_file *stl, float angle) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) { for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].z, stl_rotate(&stl->facet_start[i].vertex[j](2),
&stl->facet_start[i].vertex[j].x, c, s); &stl->facet_start[i].vertex[j](0), c, s);
} }
} }
stl_get_size(stl); stl_get_size(stl);
@ -254,8 +207,8 @@ stl_rotate_z(stl_file *stl, float angle) {
for(i = 0; i < stl->stats.number_of_facets; i++) { for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) { for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].x, stl_rotate(&stl->facet_start[i].vertex[j](0),
&stl->facet_start[i].vertex[j].y, c, s); &stl->facet_start[i].vertex[j](1), c, s);
} }
} }
stl_get_size(stl); stl_get_size(stl);
@ -272,142 +225,98 @@ stl_rotate(float *x, float *y, const double c, const double s) {
*y = float(s * xold + c * yold); *y = float(s * xold + c * yold);
} }
extern void void stl_get_size(stl_file *stl)
stl_get_size(stl_file *stl) { {
int i; if (stl->error || stl->stats.number_of_facets == 0)
int j; return;
stl->stats.min = stl->facet_start[0].vertex[0];
if (stl->error) return; stl->stats.max = stl->stats.min;
if (stl->stats.number_of_facets == 0) return; for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
const stl_facet &face = stl->facet_start[i];
stl->stats.min.x = stl->facet_start[0].vertex[0].x; for (int j = 0; j < 3; ++ j) {
stl->stats.min.y = stl->facet_start[0].vertex[0].y; stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]);
stl->stats.min.z = stl->facet_start[0].vertex[0].z; stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]);
stl->stats.max.x = stl->facet_start[0].vertex[0].x;
stl->stats.max.y = stl->facet_start[0].vertex[0].y;
stl->stats.max.z = stl->facet_start[0].vertex[0].z;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->stats.min.x = STL_MIN(stl->stats.min.x,
stl->facet_start[i].vertex[j].x);
stl->stats.min.y = STL_MIN(stl->stats.min.y,
stl->facet_start[i].vertex[j].y);
stl->stats.min.z = STL_MIN(stl->stats.min.z,
stl->facet_start[i].vertex[j].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x,
stl->facet_start[i].vertex[j].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y,
stl->facet_start[i].vertex[j].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z,
stl->facet_start[i].vertex[j].z);
} }
} }
stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; stl->stats.size = stl->stats.max - stl->stats.min;
stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; stl->stats.bounding_diameter = stl->stats.size.norm();
stl->stats.size.z = stl->stats.max.z - stl->stats.min.z;
stl->stats.bounding_diameter = sqrt(
stl->stats.size.x * stl->stats.size.x +
stl->stats.size.y * stl->stats.size.y +
stl->stats.size.z * stl->stats.size.z
);
} }
void void stl_mirror_xy(stl_file *stl)
stl_mirror_xy(stl_file *stl) { {
int i; if (stl->error)
int j; return;
float temp_size;
if (stl->error) return; for(int i = 0; i < stl->stats.number_of_facets; i++) {
for(int j = 0; j < 3; j++) {
for(i = 0; i < stl->stats.number_of_facets; i++) { stl->facet_start[i].vertex[j](2) *= -1.0;
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].z *= -1.0;
} }
} }
temp_size = stl->stats.min.z; float temp_size = stl->stats.min(2);
stl->stats.min.z = stl->stats.max.z; stl->stats.min(2) = stl->stats.max(2);
stl->stats.max.z = temp_size; stl->stats.max(2) = temp_size;
stl->stats.min.z *= -1.0; stl->stats.min(2) *= -1.0;
stl->stats.max.z *= -1.0; stl->stats.max(2) *= -1.0;
stl_reverse_all_facets(stl); stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
} }
void void stl_mirror_yz(stl_file *stl)
stl_mirror_yz(stl_file *stl) { {
int i;
int j;
float temp_size;
if (stl->error) return; if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) { for (int i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x *= -1.0; stl->facet_start[i].vertex[j](0) *= -1.0;
} }
} }
temp_size = stl->stats.min.x; float temp_size = stl->stats.min(0);
stl->stats.min.x = stl->stats.max.x; stl->stats.min(0) = stl->stats.max(0);
stl->stats.max.x = temp_size; stl->stats.max(0) = temp_size;
stl->stats.min.x *= -1.0; stl->stats.min(0) *= -1.0;
stl->stats.max.x *= -1.0; stl->stats.max(0) *= -1.0;
stl_reverse_all_facets(stl); stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
} }
void void stl_mirror_xz(stl_file *stl)
stl_mirror_xz(stl_file *stl) { {
int i; if (stl->error)
int j; return;
float temp_size;
if (stl->error) return; for (int i = 0; i < stl->stats.number_of_facets; i++) {
for (int j = 0; j < 3; j++) {
for(i = 0; i < stl->stats.number_of_facets; i++) { stl->facet_start[i].vertex[j](1) *= -1.0;
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].y *= -1.0;
} }
} }
temp_size = stl->stats.min.y; float temp_size = stl->stats.min(1);
stl->stats.min.y = stl->stats.max.y; stl->stats.min(1) = stl->stats.max(1);
stl->stats.max.y = temp_size; stl->stats.max(1) = temp_size;
stl->stats.min.y *= -1.0; stl->stats.min(1) *= -1.0;
stl->stats.max.y *= -1.0; stl->stats.max(1) *= -1.0;
stl_reverse_all_facets(stl); stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
} }
static float get_volume(stl_file *stl) { static float get_volume(stl_file *stl)
stl_vertex p0; {
stl_vertex p; if (stl->error)
stl_normal n; return 0;
float height;
float area;
float volume = 0.0;
if (stl->error) return 0; // Choose a point, any point as the reference.
stl_vertex p0 = stl->facet_start[0].vertex[0];
/* Choose a point, any point as the reference */ float volume = 0.f;
p0.x = stl->facet_start[0].vertex[0].x; for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
p0.y = stl->facet_start[0].vertex[0].y; // Do dot product to get distance from point to plane.
p0.z = stl->facet_start[0].vertex[0].z; float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
float area = get_area(&stl->facet_start[i]);
for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
p.x = stl->facet_start[i].vertex[0].x - p0.x;
p.y = stl->facet_start[i].vertex[0].y - p0.y;
p.z = stl->facet_start[i].vertex[0].z - p0.z;
/* Do dot product to get distance from point to plane */
n = stl->facet_start[i].normal;
height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z);
area = get_area(&stl->facet_start[i]);
volume += (area * height) / 3.0f; volume += (area * height) / 3.0f;
} }
return volume; return volume;
} }
void stl_calculate_volume(stl_file *stl) { void stl_calculate_volume(stl_file *stl)
{
if (stl->error) return; if (stl->error) return;
stl->stats.volume = get_volume(stl); stl->stats.volume = get_volume(stl);
if(stl->stats.volume < 0.0) { if(stl->stats.volume < 0.0) {
@ -416,35 +325,32 @@ void stl_calculate_volume(stl_file *stl) {
} }
} }
static float get_area(stl_facet *facet) { static float get_area(stl_facet *facet)
double cross[3][3]; {
float sum[3];
float n[3];
float area;
int i;
/* cast to double before calculating cross product because large coordinates /* cast to double before calculating cross product because large coordinates
can result in overflowing product can result in overflowing product
(bad area is responsible for bad volume and bad facets reversal) */ (bad area is responsible for bad volume and bad facets reversal) */
for(i = 0; i < 3; i++) { double cross[3][3];
cross[i][0]=(((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].z) - for (int i = 0; i < 3; i++) {
((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].y)); cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) -
cross[i][1]=(((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].x) - ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1)));
((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].z)); cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) -
cross[i][2]=(((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].y) - ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2)));
((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].x)); cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) -
((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0)));
} }
sum[0] = cross[0][0] + cross[1][0] + cross[2][0]; stl_normal sum;
sum[1] = cross[0][1] + cross[1][1] + cross[2][1]; sum(0) = cross[0][0] + cross[1][0] + cross[2][0];
sum[2] = cross[0][2] + cross[1][2] + cross[2][2]; sum(1) = cross[0][1] + cross[1][1] + cross[2][1];
sum(2) = cross[0][2] + cross[1][2] + cross[2][2];
/* This should already be done. But just in case, let's do it again */ // This should already be done. But just in case, let's do it again.
//FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy.
stl_normal n;
stl_calculate_normal(n, facet); stl_calculate_normal(n, facet);
stl_normalize_vector(n); stl_normalize_vector(n);
return 0.5f * n.dot(sum);
area = 0.5 * (n[0] * sum[0] + n[1] * sum[1] + n[2] * sum[2]);
return area;
} }
void stl_repair(stl_file *stl, void stl_repair(stl_file *stl,

View file

@ -7,9 +7,9 @@
namespace Slic3r { namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points); template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
template BoundingBoxBase<Pointf>::BoundingBoxBase(const std::vector<Pointf> &points); template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points);
template BoundingBox3Base<Pointf3>::BoundingBox3Base(const std::vector<Pointf3> &points); template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
BoundingBox::BoundingBox(const Lines &lines) BoundingBox::BoundingBox(const Lines &lines)
{ {
@ -22,8 +22,7 @@ BoundingBox::BoundingBox(const Lines &lines)
*this = BoundingBox(points); *this = BoundingBox(points);
} }
void void BoundingBox::polygon(Polygon* polygon) const
BoundingBox::polygon(Polygon* polygon) const
{ {
polygon->points.clear(); polygon->points.clear();
polygon->points.resize(4); polygon->points.resize(4);
@ -37,8 +36,7 @@ BoundingBox::polygon(Polygon* polygon) const
polygon->points[3](1) = this->max(1); polygon->points[3](1) = this->max(1);
} }
Polygon Polygon BoundingBox::polygon() const
BoundingBox::polygon() const
{ {
Polygon p; Polygon p;
this->polygon(&p); this->polygon(&p);
@ -72,24 +70,23 @@ BoundingBoxBase<PointClass>::scale(double factor)
this->max *= factor; this->max *= factor;
} }
template void BoundingBoxBase<Point>::scale(double factor); template void BoundingBoxBase<Point>::scale(double factor);
template void BoundingBoxBase<Pointf>::scale(double factor); template void BoundingBoxBase<Vec2d>::scale(double factor);
template void BoundingBoxBase<Pointf3>::scale(double factor); template void BoundingBoxBase<Vec3d>::scale(double factor);
template <class PointClass> void template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const PointClass &point) BoundingBoxBase<PointClass>::merge(const PointClass &point)
{ {
if (this->defined) { if (this->defined) {
this->min(0) = std::min(point(0), this->min(0)); this->min = this->min.cwiseMin(point);
this->min(1) = std::min(point(1), this->min(1)); this->max = this->max.cwiseMax(point);
this->max(0) = std::max(point(0), this->max(0));
this->max(1) = std::max(point(1), this->max(1));
} else { } else {
this->min = this->max = point; this->min = point;
this->max = point;
this->defined = true; this->defined = true;
} }
} }
template void BoundingBoxBase<Point>::merge(const Point &point); template void BoundingBoxBase<Point>::merge(const Point &point);
template void BoundingBoxBase<Pointf>::merge(const Pointf &point); template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
template <class PointClass> void template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points) BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
@ -97,7 +94,7 @@ BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
this->merge(BoundingBoxBase(points)); this->merge(BoundingBoxBase(points));
} }
template void BoundingBoxBase<Point>::merge(const Points &points); template void BoundingBoxBase<Point>::merge(const Points &points);
template void BoundingBoxBase<Pointf>::merge(const Pointfs &points); template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
template <class PointClass> void template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb) BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
@ -105,10 +102,8 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1));
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min(0) = std::min(bb.min(0), this->min(0)); this->min = this->min.cwiseMin(bb.min);
this->min(1) = std::min(bb.min(1), this->min(1)); this->max = this->max.cwiseMax(bb.max);
this->max(0) = std::max(bb.max(0), this->max(0));
this->max(1) = std::max(bb.max(1), this->max(1));
} else { } else {
this->min = bb.min; this->min = bb.min;
this->max = bb.max; this->max = bb.max;
@ -117,25 +112,28 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
} }
} }
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb); template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
template void BoundingBoxBase<Pointf>::merge(const BoundingBoxBase<Pointf> &bb); template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
template <class PointClass> void template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const PointClass &point) BoundingBox3Base<PointClass>::merge(const PointClass &point)
{ {
if (this->defined) { if (this->defined) {
this->min(2) = std::min(point(2), this->min(2)); this->min = this->min.cwiseMin(point);
this->max(2) = std::max(point(2), this->max(2)); this->max = this->max.cwiseMax(point);
} else {
this->min = point;
this->max = point;
this->defined = true;
} }
BoundingBoxBase<PointClass>::merge(point);
} }
template void BoundingBox3Base<Pointf3>::merge(const Pointf3 &point); template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
template <class PointClass> void template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points) BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
{ {
this->merge(BoundingBox3Base(points)); this->merge(BoundingBox3Base(points));
} }
template void BoundingBox3Base<Pointf3>::merge(const Pointf3s &points); template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
template <class PointClass> void template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb) BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
@ -143,13 +141,16 @@ BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2));
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min(2) = std::min(bb.min(2), this->min(2)); this->min = this->min.cwiseMin(bb.min);
this->max(2) = std::max(bb.max(2), this->max(2)); this->max = this->max.cwiseMax(bb.max);
} else {
this->min = bb.min;
this->max = bb.max;
this->defined = true;
} }
BoundingBoxBase<PointClass>::merge(bb);
} }
} }
template void BoundingBox3Base<Pointf3>::merge(const BoundingBox3Base<Pointf3> &bb); template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
template <class PointClass> PointClass template <class PointClass> PointClass
BoundingBoxBase<PointClass>::size() const BoundingBoxBase<PointClass>::size() const
@ -157,14 +158,14 @@ BoundingBoxBase<PointClass>::size() const
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1));
} }
template Point BoundingBoxBase<Point>::size() const; template Point BoundingBoxBase<Point>::size() const;
template Pointf BoundingBoxBase<Pointf>::size() const; template Vec2d BoundingBoxBase<Vec2d>::size() const;
template <class PointClass> PointClass template <class PointClass> PointClass
BoundingBox3Base<PointClass>::size() const BoundingBox3Base<PointClass>::size() const
{ {
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2));
} }
template Pointf3 BoundingBox3Base<Pointf3>::size() const; template Vec3d BoundingBox3Base<Vec3d>::size() const;
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
{ {
@ -174,7 +175,7 @@ template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
return 0.5 * sqrt(x*x+y*y); return 0.5 * sqrt(x*x+y*y);
} }
template double BoundingBoxBase<Point>::radius() const; template double BoundingBoxBase<Point>::radius() const;
template double BoundingBoxBase<Pointf>::radius() const; template double BoundingBoxBase<Vec2d>::radius() const;
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const template <class PointClass> double BoundingBox3Base<PointClass>::radius() const
{ {
@ -183,7 +184,7 @@ template <class PointClass> double BoundingBox3Base<PointClass>::radius() const
double z = this->max(2) - this->min(2); double z = this->max(2) - this->min(2);
return 0.5 * sqrt(x*x+y*y+z*z); return 0.5 * sqrt(x*x+y*y+z*z);
} }
template double BoundingBox3Base<Pointf3>::radius() const; template double BoundingBox3Base<Vec3d>::radius() const;
template <class PointClass> void template <class PointClass> void
BoundingBoxBase<PointClass>::offset(coordf_t delta) BoundingBoxBase<PointClass>::offset(coordf_t delta)
@ -193,7 +194,7 @@ BoundingBoxBase<PointClass>::offset(coordf_t delta)
this->max += v; this->max += v;
} }
template void BoundingBoxBase<Point>::offset(coordf_t delta); template void BoundingBoxBase<Point>::offset(coordf_t delta);
template void BoundingBoxBase<Pointf>::offset(coordf_t delta); template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
template <class PointClass> void template <class PointClass> void
BoundingBox3Base<PointClass>::offset(coordf_t delta) BoundingBox3Base<PointClass>::offset(coordf_t delta)
@ -202,29 +203,22 @@ BoundingBox3Base<PointClass>::offset(coordf_t delta)
this->min -= v; this->min -= v;
this->max += v; this->max += v;
} }
template void BoundingBox3Base<Pointf3>::offset(coordf_t delta); template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
template <class PointClass> PointClass template <class PointClass> PointClass
BoundingBoxBase<PointClass>::center() const BoundingBoxBase<PointClass>::center() const
{ {
return PointClass( return (this->min + this->max) / 2;
(this->max(0) + this->min(0))/2,
(this->max(1) + this->min(1))/2
);
} }
template Point BoundingBoxBase<Point>::center() const; template Point BoundingBoxBase<Point>::center() const;
template Pointf BoundingBoxBase<Pointf>::center() const; template Vec2d BoundingBoxBase<Vec2d>::center() const;
template <class PointClass> PointClass template <class PointClass> PointClass
BoundingBox3Base<PointClass>::center() const BoundingBox3Base<PointClass>::center() const
{ {
return PointClass( return (this->min + this->max) / 2;
(this->max(0) + this->min(0))/2,
(this->max(1) + this->min(1))/2,
(this->max(2) + this->min(2))/2
);
} }
template Pointf3 BoundingBox3Base<Pointf3>::center() const; template Vec3d BoundingBox3Base<Vec3d>::center() const;
template <class PointClass> coordf_t template <class PointClass> coordf_t
BoundingBox3Base<PointClass>::max_size() const BoundingBox3Base<PointClass>::max_size() const
@ -232,7 +226,7 @@ BoundingBox3Base<PointClass>::max_size() const
PointClass s = size(); PointClass s = size();
return std::max(s(0), std::max(s(1), s(2))); return std::max(s(0), std::max(s(1), s(2)));
} }
template coordf_t BoundingBox3Base<Pointf3>::max_size() const; template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
// Align a coordinate to a grid. The coordinate may be negative, // Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one. // the aligned value will never be bigger than the original one.
@ -255,39 +249,35 @@ void BoundingBox::align_to_grid(const coord_t cell_size)
} }
} }
BoundingBoxf3 BoundingBoxf3::transformed(const Transform3f& matrix) const BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const
{ {
Eigen::Matrix<float, 3, 8, Eigen::DontAlign> vertices; typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices;
vertices(0, 0) = (float)min(0); vertices(1, 0) = (float)min(1); vertices(2, 0) = (float)min(2); Vertices src_vertices;
vertices(0, 1) = (float)max(0); vertices(1, 1) = (float)min(1); vertices(2, 1) = (float)min(2); src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2);
vertices(0, 2) = (float)max(0); vertices(1, 2) = (float)max(1); vertices(2, 2) = (float)min(2); src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2);
vertices(0, 3) = (float)min(0); vertices(1, 3) = (float)max(1); vertices(2, 3) = (float)min(2); src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2);
vertices(0, 4) = (float)min(0); vertices(1, 4) = (float)min(1); vertices(2, 4) = (float)max(2); src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2);
vertices(0, 5) = (float)max(0); vertices(1, 5) = (float)min(1); vertices(2, 5) = (float)max(2); src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2);
vertices(0, 6) = (float)max(0); vertices(1, 6) = (float)max(1); vertices(2, 6) = (float)max(2); src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2);
vertices(0, 7) = (float)min(0); vertices(1, 7) = (float)max(1); vertices(2, 7) = (float)max(2); src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2);
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2);
Eigen::Matrix<float, 3, 8, Eigen::DontAlign> transf_vertices = matrix * vertices.colwise().homogeneous(); Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous();
float min_x = transf_vertices(0, 0); Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
float max_x = transf_vertices(0, 0); Vec3d v_max = v_min;
float min_y = transf_vertices(1, 0);
float max_y = transf_vertices(1, 0);
float min_z = transf_vertices(2, 0);
float max_z = transf_vertices(2, 0);
for (int i = 1; i < 8; ++i) for (int i = 1; i < 8; ++i)
{ {
min_x = std::min(min_x, transf_vertices(0, i)); for (int j = 0; j < 3; ++j)
max_x = std::max(max_x, transf_vertices(0, i)); {
min_y = std::min(min_y, transf_vertices(1, i)); v_min(j) = std::min(v_min(j), dst_vertices(j, i));
max_y = std::max(max_y, transf_vertices(1, i)); v_max(j) = std::max(v_max(j), dst_vertices(j, i));
min_z = std::min(min_z, transf_vertices(2, i)); }
max_z = std::max(max_z, transf_vertices(2, i));
} }
return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z)); return BoundingBoxf3(v_min, v_max);
} }
} }

View file

@ -7,11 +7,6 @@
namespace Slic3r { namespace Slic3r {
typedef Point Size;
typedef Point3 Size3;
typedef Pointf Sizef;
typedef Pointf3 Sizef3;
template <class PointClass> template <class PointClass>
class BoundingBoxBase class BoundingBoxBase
{ {
@ -20,23 +15,20 @@ public:
PointClass max; PointClass max;
bool defined; bool defined;
BoundingBoxBase() : defined(false) {}; BoundingBoxBase() : defined(false), min(PointClass::Zero()), max(PointClass::Zero()) {}
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {} min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
BoundingBoxBase(const std::vector<PointClass>& points) BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
{ {
if (points.empty()) if (points.empty())
CONFESS("Empty point set supplied to BoundingBoxBase constructor"); CONFESS("Empty point set supplied to BoundingBoxBase constructor");
typename std::vector<PointClass>::const_iterator it = points.begin(); typename std::vector<PointClass>::const_iterator it = points.begin();
this->min(0) = this->max(0) = (*it)(0); this->min = *it;
this->min(1) = this->max(1) = (*it)(1); this->max = *it;
for (++it; it != points.end(); ++it) for (++ it; it != points.end(); ++ it) {
{ this->min = this->min.cwiseMin(*it);
this->min(0) = std::min((*it)(0), this->min(0)); this->max = this->max.cwiseMax(*it);
this->min(1) = std::min((*it)(1), this->min(1));
this->max(0) = std::max((*it)(0), this->max(0));
this->max(1) = std::max((*it)(1), this->max(1));
} }
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)); this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
} }
@ -47,7 +39,7 @@ public:
PointClass size() const; PointClass size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
void translate(const Pointf &v) { this->min += v; this->max += v; } void translate(const Vec2d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
PointClass center() const; PointClass center() const;
bool contains(const PointClass &point) const { bool contains(const PointClass &point) const {
@ -71,19 +63,17 @@ public:
BoundingBoxBase<PointClass>(pmin, pmax) BoundingBoxBase<PointClass>(pmin, pmax)
{ if (pmin(2) >= pmax(2)) BoundingBoxBase<PointClass>::defined = false; } { if (pmin(2) >= pmax(2)) BoundingBoxBase<PointClass>::defined = false; }
BoundingBox3Base(const std::vector<PointClass>& points) BoundingBox3Base(const std::vector<PointClass>& points)
: BoundingBoxBase<PointClass>(points)
{ {
if (points.empty()) if (points.empty())
CONFESS("Empty point set supplied to BoundingBox3Base constructor"); CONFESS("Empty point set supplied to BoundingBox3Base constructor");
typename std::vector<PointClass>::const_iterator it = points.begin(); typename std::vector<PointClass>::const_iterator it = points.begin();
this->min(2) = this->max(2) = (*it)(2); this->min = *it;
for (++it; it != points.end(); ++it) this->max = *it;
{ for (++ it; it != points.end(); ++ it) {
this->min(2) = std::min((*it)(2), this->min(2)); this->min = this->min.cwiseMin(*it);
this->max(2) = std::max((*it)(2), this->max(2)); this->max = this->max.cwiseMax(*it);
} }
this->defined &= (this->min(2) < this->max(2)); this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2));
} }
void merge(const PointClass &point); void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points); void merge(const std::vector<PointClass> &points);
@ -91,7 +81,7 @@ public:
PointClass size() const; PointClass size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
void translate(const Pointf3 &v) { this->min += v; this->max += v; } void translate(const Vec3d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
PointClass center() const; PointClass center() const;
coordf_t max_size() const; coordf_t max_size() const;
@ -130,30 +120,30 @@ public:
friend BoundingBox get_extents_rotated(const Points &points, double angle); friend BoundingBox get_extents_rotated(const Points &points, double angle);
}; };
class BoundingBox3 : public BoundingBox3Base<Point3> class BoundingBox3 : public BoundingBox3Base<Vec3crd>
{ {
public: public:
BoundingBox3() : BoundingBox3Base<Point3>() {}; BoundingBox3() : BoundingBox3Base<Vec3crd>() {};
BoundingBox3(const Point3 &pmin, const Point3 &pmax) : BoundingBox3Base<Point3>(pmin, pmax) {}; BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base<Vec3crd>(pmin, pmax) {};
BoundingBox3(const Points3& points) : BoundingBox3Base<Point3>(points) {}; BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>(points) {};
}; };
class BoundingBoxf : public BoundingBoxBase<Pointf> class BoundingBoxf : public BoundingBoxBase<Vec2d>
{ {
public: public:
BoundingBoxf() : BoundingBoxBase<Pointf>() {}; BoundingBoxf() : BoundingBoxBase<Vec2d>() {};
BoundingBoxf(const Pointf &pmin, const Pointf &pmax) : BoundingBoxBase<Pointf>(pmin, pmax) {}; BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase<Vec2d>(pmin, pmax) {};
BoundingBoxf(const std::vector<Pointf> &points) : BoundingBoxBase<Pointf>(points) {}; BoundingBoxf(const std::vector<Vec2d> &points) : BoundingBoxBase<Vec2d>(points) {};
}; };
class BoundingBoxf3 : public BoundingBox3Base<Pointf3> class BoundingBoxf3 : public BoundingBox3Base<Vec3d>
{ {
public: public:
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {}; BoundingBoxf3() : BoundingBox3Base<Vec3d>() {};
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {}; BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base<Vec3d>(pmin, pmax) {};
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {}; BoundingBoxf3(const std::vector<Vec3d> &points) : BoundingBox3Base<Vec3d>(points) {};
BoundingBoxf3 transformed(const Transform3f& matrix) const; BoundingBoxf3 transformed(const Transform3d& matrix) const;
}; };
template<typename VT> template<typename VT>

View file

@ -49,9 +49,9 @@ enum ConfigOptionType {
coPercents = coPercent + coVectorType, coPercents = coPercent + coVectorType,
// a fraction or an absolute value // a fraction or an absolute value
coFloatOrPercent = 5, coFloatOrPercent = 5,
// single 2d point. Currently not used. // single 2d point (Point2f). Currently not used.
coPoint = 6, coPoint = 6,
// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
coPoints = coPoint + coVectorType, coPoints = coPoint + coVectorType,
// single boolean value // single boolean value
coBool = 7, coBool = 7,
@ -622,11 +622,11 @@ public:
} }
}; };
class ConfigOptionPoint : public ConfigOptionSingle<Pointf> class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
{ {
public: public:
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {} ConfigOptionPoint() : ConfigOptionSingle<Vec2d>(Vec2d(0,0)) {}
explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle<Pointf>(value) {} explicit ConfigOptionPoint(const Vec2d &value) : ConfigOptionSingle<Vec2d>(value) {}
static ConfigOptionType static_type() { return coPoint; } static ConfigOptionType static_type() { return coPoint; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
@ -652,13 +652,13 @@ public:
} }
}; };
class ConfigOptionPoints : public ConfigOptionVector<Pointf> class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
{ {
public: public:
ConfigOptionPoints() : ConfigOptionVector<Pointf>() {} ConfigOptionPoints() : ConfigOptionVector<Vec2d>() {}
explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector<Pointf>(n, value) {} explicit ConfigOptionPoints(size_t n, const Vec2d &value) : ConfigOptionVector<Vec2d>(n, value) {}
explicit ConfigOptionPoints(std::initializer_list<Pointf> il) : ConfigOptionVector<Pointf>(std::move(il)) {} explicit ConfigOptionPoints(std::initializer_list<Vec2d> il) : ConfigOptionVector<Vec2d>(std::move(il)) {}
explicit ConfigOptionPoints(const std::vector<Pointf> &values) : ConfigOptionVector<Pointf>(values) {} explicit ConfigOptionPoints(const std::vector<Vec2d> &values) : ConfigOptionVector<Vec2d>(values) {}
static ConfigOptionType static_type() { return coPoints; } static ConfigOptionType static_type() { return coPoints; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
@ -696,7 +696,7 @@ public:
std::istringstream is(str); std::istringstream is(str);
std::string point_str; std::string point_str;
while (std::getline(is, point_str, ',')) { while (std::getline(is, point_str, ',')) {
Pointf point; Vec2d point(Vec2d::Zero());
std::istringstream iss(point_str); std::istringstream iss(point_str);
std::string coord_str; std::string coord_str;
if (std::getline(iss, coord_str, 'x')) { if (std::getline(iss, coord_str, 'x')) {
@ -821,12 +821,7 @@ public:
bool deserialize(const std::string &str, bool append = false) override bool deserialize(const std::string &str, bool append = false) override
{ {
UNUSED(append); UNUSED(append);
const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values(); return from_string(str, this->value);
auto it = enum_keys_map.find(str);
if (it == enum_keys_map.end())
return false;
this->value = static_cast<T>(it->second);
return true;
} }
static bool has(T value) static bool has(T value)
@ -838,7 +833,7 @@ public:
} }
// Map from an enum name to an enum integer value. // Map from an enum name to an enum integer value.
static t_config_enum_names& get_enum_names() static const t_config_enum_names& get_enum_names()
{ {
static t_config_enum_names names; static t_config_enum_names names;
if (names.empty()) { if (names.empty()) {
@ -855,7 +850,17 @@ public:
return names; return names;
} }
// Map from an enum name to an enum integer value. // Map from an enum name to an enum integer value.
static t_config_enum_values& get_enum_values(); static const t_config_enum_values& get_enum_values();
static bool from_string(const std::string &str, T &value)
{
const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
auto it = enum_keys_map.find(str);
if (it == enum_keys_map.end())
return false;
value = static_cast<T>(it->second);
return true;
}
}; };
// Generic enum configuration value. // Generic enum configuration value.
@ -900,7 +905,7 @@ public:
// What type? bool, int, string etc. // What type? bool, int, string etc.
ConfigOptionType type = coNone; ConfigOptionType type = coNone;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
ConfigOption *default_value = nullptr; const ConfigOption *default_value = nullptr;
// Usually empty. // Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@ -958,7 +963,7 @@ public:
std::vector<std::string> enum_labels; std::vector<std::string> enum_labels;
// For enums (when type == coEnum). Maps enum_values to enums. // For enums (when type == coEnum). Maps enum_values to enums.
// Initialized by ConfigOptionEnum<xxx>::get_enum_values() // Initialized by ConfigOptionEnum<xxx>::get_enum_values()
t_config_enum_values *enum_keys_map = nullptr; const t_config_enum_values *enum_keys_map = nullptr;
bool has_enum_value(const std::string &value) const { bool has_enum_value(const std::string &value) const {
for (const std::string &v : enum_values) for (const std::string &v : enum_values)

View file

@ -149,7 +149,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; } double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; } Polyline as_polyline() const { return this->polyline; }
virtual double total_volume() const { return mm3_per_mm * unscale(length()); } virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
private: private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;

View file

@ -54,7 +54,7 @@ static std::vector<coordf_t> perpendPoints(const coordf_t offset, const size_t b
// components that are outside these limits are set to the limits. // components that are outside these limits are set to the limits.
static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY) static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY)
{ {
for (Pointf &pt : pts) { for (Vec2d &pt : pts) {
pt(0) = clamp(minX, maxX, pt(0)); pt(0) = clamp(minX, maxX, pt(0));
pt(1) = clamp(minY, maxY, pt(1)); pt(1) = clamp(minY, maxY, pt(1));
} }
@ -66,7 +66,7 @@ static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coor
Pointfs out; Pointfs out;
out.reserve(x.size()); out.reserve(x.size());
for (size_t i = 0; i < x.size(); ++ i) for (size_t i = 0; i < x.size(); ++ i)
out.push_back(Pointf(x[i], y[i])); out.push_back(Vec2d(x[i], y[i]));
return out; return out;
} }

View file

@ -21,7 +21,7 @@ void FillConcentric::_fill_surface_single(
if (params.density > 0.9999f && !params.dont_adjust) { if (params.density > 0.9999f && !params.dont_adjust) {
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance); distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
this->spacing = unscale(distance); this->spacing = unscale<double>(distance);
} }
Polygons loops = (Polygons)expolygon; Polygons loops = (Polygons)expolygon;

View file

@ -30,15 +30,15 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool
} }
static inline Polyline make_wave( static inline Polyline make_wave(
const std::vector<Pointf>& one_period, double width, double height, double offset, double scaleFactor, const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
double z_cos, double z_sin, bool vertical) double z_cos, double z_sin, bool vertical)
{ {
std::vector<Pointf> points = one_period; std::vector<Vec2d> points = one_period;
double period = points.back()(0); double period = points.back()(0);
points.pop_back(); points.pop_back();
int n = points.size(); int n = points.size();
do { do {
points.emplace_back(Pointf(points[points.size()-n](0) + period, points[points.size()-n](1))); points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
} while (points.back()(0) < width); } while (points.back()(0) < width);
points.back()(0) = width; points.back()(0) = width;
@ -55,14 +55,14 @@ static inline Polyline make_wave(
return polyline; return polyline;
} }
static std::vector<Pointf> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip)
{ {
std::vector<Pointf> points; std::vector<Vec2d> points;
double dx = M_PI_4; // very coarse spacing to begin with double dx = M_PI_4; // very coarse spacing to begin with
double limit = std::min(2*M_PI, width); double limit = std::min(2*M_PI, width);
for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
x = std::min(x, limit); x = std::min(x, limit);
points.emplace_back(Pointf(x,f(x, z_sin,z_cos, vertical, flip))); points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip)));
} }
// now we will check all internal points and in case some are too far from the line connecting its neighbours, // now we will check all internal points and in case some are too far from the line connecting its neighbours,
@ -71,17 +71,19 @@ static std::vector<Pointf> make_one_period(double width, double scaleFactor, dou
for (unsigned int i=1;i<points.size()-1;++i) { for (unsigned int i=1;i<points.size()-1;++i) {
auto& lp = points[i-1]; // left point auto& lp = points[i-1]; // left point
auto& tp = points[i]; // this point auto& tp = points[i]; // this point
Vec2d lrv = tp - lp;
auto& rp = points[i+1]; // right point auto& rp = points[i+1]; // right point
// calculate distance of the point to the line: // calculate distance of the point to the line:
double dist_mm = unscale(scaleFactor * std::abs( (rp(1) - lp(1))*tp(0) + (lp(0) - rp(0))*tp(1) + (rp(0)*lp(1) - rp(1)*lp(0)) ) / std::hypot((rp(1) - lp(1)),(lp(0) - rp(0)))); double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm();
if (dist_mm > tolerance) { // if the difference from straight line is more than this if (dist_mm > tolerance) { // if the difference from straight line is more than this
double x = 0.5f * (points[i-1](0) + points[i](0)); double x = 0.5f * (points[i-1](0) + points[i](0));
points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip))); points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
x = 0.5f * (points[i+1](0) + points[i](0)); x = 0.5f * (points[i+1](0) + points[i](0));
points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip))); points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
std::sort(points.begin(), points.end()); // we added the points to the end, but need them all in order // we added the points to the end, but need them all in order
--i; // decrement i so we also check the first newly added point std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; });
// decrement i so we also check the first newly added point
--i;
} }
} }
return points; return points;
@ -107,7 +109,7 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
std::swap(width,height); std::swap(width,height);
} }
std::vector<Pointf> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
Polylines result; Polylines result;
for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines

View file

@ -86,12 +86,12 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
coordf_t r = 1; coordf_t r = 1;
Pointfs out; Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center. //FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
out.push_back(Pointf(0, 0)); out.push_back(Vec2d(0, 0));
out.push_back(Pointf(1, 0)); out.push_back(Vec2d(1, 0));
while (r < rmax) { while (r < rmax) {
theta += 1. / r; theta += 1. / r;
r = a + b * theta; r = a + b * theta;
out.push_back(Pointf(r * cos(theta), r * sin(theta))); out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
} }
return out; return out;
} }
@ -162,7 +162,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
line.reserve(sz2); line.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) { for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i); Point p = hilbert_n_to_xy(i);
line.push_back(Pointf(p(0) + min_x, p(1) + min_y)); line.push_back(Vec2d(p(0) + min_x, p(1) + min_y));
} }
return line; return line;
} }
@ -175,27 +175,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_
coordf_t r = 0; coordf_t r = 0;
coordf_t r_inc = sqrt(2.); coordf_t r_inc = sqrt(2.);
Pointfs out; Pointfs out;
out.push_back(Pointf(0, 0)); out.push_back(Vec2d(0, 0));
while (r < rmax) { while (r < rmax) {
r += r_inc; r += r_inc;
coordf_t rx = r / sqrt(2.); coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx; coordf_t r2 = r + rx;
out.push_back(Pointf( r, 0.)); out.push_back(Vec2d( r, 0.));
out.push_back(Pointf( r2, rx)); out.push_back(Vec2d( r2, rx));
out.push_back(Pointf( rx, rx)); out.push_back(Vec2d( rx, rx));
out.push_back(Pointf( rx, r2)); out.push_back(Vec2d( rx, r2));
out.push_back(Pointf(0., r)); out.push_back(Vec2d(0., r));
out.push_back(Pointf(-rx, r2)); out.push_back(Vec2d(-rx, r2));
out.push_back(Pointf(-rx, rx)); out.push_back(Vec2d(-rx, rx));
out.push_back(Pointf(-r2, rx)); out.push_back(Vec2d(-r2, rx));
out.push_back(Pointf(-r, 0.)); out.push_back(Vec2d(-r, 0.));
out.push_back(Pointf(-r2, -rx)); out.push_back(Vec2d(-r2, -rx));
out.push_back(Pointf(-rx, -rx)); out.push_back(Vec2d(-rx, -rx));
out.push_back(Pointf(-rx, -r2)); out.push_back(Vec2d(-rx, -r2));
out.push_back(Pointf(0., -r)); out.push_back(Vec2d(0., -r));
out.push_back(Pointf( rx, -r2)); out.push_back(Vec2d( rx, -r2));
out.push_back(Pointf( rx, -rx)); out.push_back(Vec2d( rx, -rx));
out.push_back(Pointf( r2+r_inc, -rx)); out.push_back(Vec2d( r2+r_inc, -rx));
} }
return out; return out;
} }

View file

@ -27,7 +27,7 @@ void FillRectilinear::_fill_surface_single(
// define flow spacing according to requested density // define flow spacing according to requested density
if (params.density > 0.9999f && !params.dont_adjust) { if (params.density > 0.9999f && !params.dont_adjust) {
this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing); this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing);
this->spacing = unscale(this->_line_spacing); this->spacing = unscale<double>(this->_line_spacing);
} else { } else {
// extend bounding box so that our pattern will be aligned with other layers // extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system. // Transform the reference point to the rotated coordinate system.

View file

@ -792,7 +792,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// define flow spacing according to requested density // define flow spacing according to requested density
if (params.full_infill() && !params.dont_adjust) { if (params.full_infill() && !params.dont_adjust) {
line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing); line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
this->spacing = unscale(line_spacing); this->spacing = unscale<double>(line_spacing);
} else { } else {
// extend bounding box so that our pattern will be aligned with other layers // extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system. // Transform the reference point to the rotated coordinate system.

View file

@ -217,11 +217,11 @@ Point SegmentIntersection::pos() const
const Point &seg_start = poly.points[(this->iSegment == 0) ? poly.points.size() - 1 : this->iSegment - 1]; const Point &seg_start = poly.points[(this->iSegment == 0) ? poly.points.size() - 1 : this->iSegment - 1];
const Point &seg_end = poly.points[this->iSegment]; const Point &seg_end = poly.points[this->iSegment];
// Point, vector of the segment. // Point, vector of the segment.
const Pointf p1(seg_start.cast<coordf_t>()); const Vec2d p1(seg_start.cast<coordf_t>());
const Pointf v1((seg_end - seg_start).cast<coordf_t>()); const Vec2d v1((seg_end - seg_start).cast<coordf_t>());
// Point, vector of this hatching line. // Point, vector of this hatching line.
const Pointf p2(line->pos.cast<coordf_t>()); const Vec2d p2(line->pos.cast<coordf_t>());
const Pointf v2(line->dir.cast<coordf_t>()); const Vec2d v2(line->dir.cast<coordf_t>());
// Intersect the two rays. // Intersect the two rays.
double denom = v1(0) * v2(1) - v2(0) * v1(1); double denom = v1(0) * v2(1) - v2(0) * v1(1);
Point out; Point out;
@ -391,7 +391,7 @@ static bool prepare_infill_hatching_segments(
// Full infill, adjust the line spacing to fit an integer number of lines. // Full infill, adjust the line spacing to fit an integer number of lines.
out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size()(0), line_spacing); out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
// Report back the adjusted line spacing. // Report back the adjusted line spacing.
fill_dir_params.spacing = float(unscale(line_spacing)); fill_dir_params.spacing = unscale<double>(line_spacing);
} else { } else {
// Extend bounding box so that our pattern will be aligned with the other layers. // Extend bounding box so that our pattern will be aligned with the other layers.
// Transform the reference point to the rotated coordinate system. // Transform the reference point to the rotated coordinate system.

View file

@ -1485,12 +1485,13 @@ namespace Slic3r {
stl_facet& facet = stl.facet_start[i]; stl_facet& facet = stl.facet_start[i];
for (unsigned int v = 0; v < 3; ++v) for (unsigned int v = 0; v < 3; ++v)
{ {
::memcpy((void*)&facet.vertex[v].x, (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float)); ::memcpy(facet.vertex[v].data(), (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float));
} }
} }
stl_get_size(&stl); stl_get_size(&stl);
volume->mesh.repair(); volume->mesh.repair();
volume->calculate_convex_hull();
// apply volume's name and config data // apply volume's name and config data
for (const Metadata& metadata : volume_data.metadata) for (const Metadata& metadata : volume_data.metadata)
@ -1844,9 +1845,9 @@ namespace Slic3r {
for (int i = 0; i < stl.stats.shared_vertices; ++i) for (int i = 0; i < stl.stats.shared_vertices; ++i)
{ {
stream << " <" << VERTEX_TAG << " "; stream << " <" << VERTEX_TAG << " ";
stream << "x=\"" << stl.v_shared[i].x << "\" "; stream << "x=\"" << stl.v_shared[i](0) << "\" ";
stream << "y=\"" << stl.v_shared[i].y << "\" "; stream << "y=\"" << stl.v_shared[i](1) << "\" ";
stream << "z=\"" << stl.v_shared[i].z << "\" />\n"; stream << "z=\"" << stl.v_shared[i](2) << "\" />\n";
} }
} }

View file

@ -402,10 +402,11 @@ void AMFParserContext::endElement(const char * /* name */)
for (size_t i = 0; i < m_volume_facets.size();) { for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3]; stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++ v) for (unsigned int v = 0; v < 3; ++ v)
memcpy(&facet.vertex[v].x, &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
} }
stl_get_size(&stl); stl_get_size(&stl);
m_volume->mesh.repair(); m_volume->mesh.repair();
m_volume->calculate_convex_hull();
m_volume_facets.clear(); m_volume_facets.clear();
m_volume = nullptr; m_volume = nullptr;
break; break;
@ -760,9 +761,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) { for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) {
stream << " <vertex>\n"; stream << " <vertex>\n";
stream << " <coordinates>\n"; stream << " <coordinates>\n";
stream << " <x>" << stl.v_shared[i].x << "</x>\n"; stream << " <x>" << stl.v_shared[i](0) << "</x>\n";
stream << " <y>" << stl.v_shared[i].y << "</y>\n"; stream << " <y>" << stl.v_shared[i](1) << "</y>\n";
stream << " <z>" << stl.v_shared[i].z << "</z>\n"; stream << " <z>" << stl.v_shared[i](2) << "</z>\n";
stream << " </coordinates>\n"; stream << " </coordinates>\n";
stream << " </vertex>\n"; stream << " </vertex>\n";
} }

View file

@ -57,14 +57,14 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
continue; continue;
stl_facet &facet = stl.facet_start[i_face ++]; stl_facet &facet = stl.facet_start[i_face ++];
size_t num_normals = 0; size_t num_normals = 0;
stl_normal normal = { 0.f }; stl_normal normal(stl_normal::Zero());
for (unsigned int v = 0; v < 3; ++ v) { for (unsigned int v = 0; v < 3; ++ v) {
const ObjParser::ObjVertex &vertex = data.vertices[i++]; const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(&facet.vertex[v].x, &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float)); memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
if (vertex.normalIdx != -1) { if (vertex.normalIdx != -1) {
normal.x += data.normals[vertex.normalIdx*3]; normal(0) += data.normals[vertex.normalIdx*3];
normal.y += data.normals[vertex.normalIdx*3+1]; normal(1) += data.normals[vertex.normalIdx*3+1];
normal.z += data.normals[vertex.normalIdx*3+2]; normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals; ++ num_normals;
} }
} }
@ -74,33 +74,27 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
facet2.vertex[0] = facet.vertex[0]; facet2.vertex[0] = facet.vertex[0];
facet2.vertex[1] = facet.vertex[2]; facet2.vertex[1] = facet.vertex[2];
const ObjParser::ObjVertex &vertex = data.vertices[i++]; const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(&facet2.vertex[2].x, &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float)); memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
if (vertex.normalIdx != -1) { if (vertex.normalIdx != -1) {
normal.x += data.normals[vertex.normalIdx*3]; normal(0) += data.normals[vertex.normalIdx*3];
normal.y += data.normals[vertex.normalIdx*3+1]; normal(1) += data.normals[vertex.normalIdx*3+1];
normal.z += data.normals[vertex.normalIdx*3+2]; normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals; ++ num_normals;
} }
if (num_normals == 4) { if (num_normals == 4) {
// Normalize an average normal of a quad. // Normalize an average normal of a quad.
float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z); float len = facet.normal.norm();
if (len > EPSILON) { if (len > EPSILON) {
normal.x /= len; normal /= len;
normal.y /= len;
normal.z /= len;
facet.normal = normal; facet.normal = normal;
facet2.normal = normal; facet2.normal = normal;
} }
} }
} else if (num_normals == 3) { } else if (num_normals == 3) {
// Normalize an average normal of a triangle. // Normalize an average normal of a triangle.
float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z); float len = facet.normal.norm();
if (len > EPSILON) { if (len > EPSILON)
normal.x /= len; facet.normal = normal / len;
normal.y /= len;
normal.z /= len;
facet.normal = normal;
}
} }
} }
stl_get_size(&stl); stl_get_size(&stl);

View file

@ -166,7 +166,7 @@ bool load_prus(const char *path, Model *model)
float trafo[3][4] = { 0 }; float trafo[3][4] = { 0 };
double instance_rotation = 0.; double instance_rotation = 0.;
double instance_scaling_factor = 1.f; double instance_scaling_factor = 1.f;
Pointf instance_offset(0., 0.); Vec2d instance_offset(0., 0.);
bool trafo_set = false; bool trafo_set = false;
unsigned int group_id = (unsigned int)-1; unsigned int group_id = (unsigned int)-1;
unsigned int extruder_id = (unsigned int)-1; unsigned int extruder_id = (unsigned int)-1;
@ -260,8 +260,8 @@ bool load_prus(const char *path, Model *model)
mesh.repair(); mesh.repair();
// Transform the model. // Transform the model.
stl_transform(&stl, &trafo[0][0]); stl_transform(&stl, &trafo[0][0]);
if (std::abs(stl.stats.min.z) < EPSILON) if (std::abs(stl.stats.min(2)) < EPSILON)
stl.stats.min.z = 0.; stl.stats.min(2) = 0.;
// Add a mesh to a model. // Add a mesh to a model.
if (mesh.facets_count() > 0) if (mesh.facets_count() > 0)
mesh_valid = true; mesh_valid = true;
@ -309,11 +309,11 @@ bool load_prus(const char *path, Model *model)
assert(res_normal == 3); assert(res_normal == 3);
int res_outer_loop = line_reader.next_line_scanf(" outer loop"); int res_outer_loop = line_reader.next_line_scanf(" outer loop");
assert(res_outer_loop == 0); assert(res_outer_loop == 0);
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3); assert(res_vertex1 == 3);
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3); assert(res_vertex2 == 3);
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3); assert(res_vertex3 == 3);
int res_endloop = line_reader.next_line_scanf(" endloop"); int res_endloop = line_reader.next_line_scanf(" endloop");
assert(res_endloop == 0); assert(res_endloop == 0);
@ -324,9 +324,9 @@ bool load_prus(const char *path, Model *model)
break; break;
} }
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored? // Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it. // Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal)); memset(&facet.normal, 0, sizeof(facet.normal));

View file

@ -64,7 +64,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
// move to the nearest standby point // move to the nearest standby point
if (!this->standby_points.empty()) { if (!this->standby_points.empty()) {
// get current position in print coordinates // get current position in print coordinates
Pointf3 writer_pos = gcodegen.writer().get_position(); Vec3d writer_pos = gcodegen.writer().get_position();
Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); Point pos = Point::new_scale(writer_pos(0), writer_pos(1));
// find standby point // find standby point
@ -74,7 +74,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
/* We don't call gcodegen.travel_to() because we don't need retraction (it was already /* We don't call gcodegen.travel_to() because we don't need retraction (it was already
triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates
of the destination point must not be transformed by origin nor current extruder offset. */ of the destination point must not be transformed by origin nor current extruder offset. */
gcode += gcodegen.writer().travel_to_xy(Pointf::new_unscale(standby_point), gcode += gcodegen.writer().travel_to_xy(unscale(standby_point),
"move to standby position"); "move to standby position");
} }
@ -207,7 +207,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
check_add_eol(gcode); check_add_eol(gcode);
} }
// A phony move to the end position at the wipe tower. // A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y)); gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe. // Prepare a future wipe.
@ -293,7 +293,7 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
gcodegen.writer().toolchange(current_extruder_id); gcodegen.writer().toolchange(current_extruder_id);
gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
// A phony move to the end position at the wipe tower. // A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y)); gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
// Prepare a future wipe. // Prepare a future wipe.
gcodegen.m_wipe.path.points.clear(); gcodegen.m_wipe.path.points.clear();
@ -783,7 +783,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points);
Polygons skirts; Polygons skirts;
for (unsigned int extruder_id : print.extruders()) { for (unsigned int extruder_id : print.extruders()) {
const Pointf &extruder_offset = print.config.extruder_offset.get_at(extruder_id); const Vec2d &extruder_offset = print.config.extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt); Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1))); s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
skirts.emplace_back(std::move(s)); skirts.emplace_back(std::move(s));
@ -831,7 +831,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
final_extruder_id = tool_ordering.last_extruder(); final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1); assert(final_extruder_id != (unsigned int)-1);
} }
this->set_origin(unscale(copy(0)), unscale(copy(1))); this->set_origin(unscale(copy));
if (finished_objects > 0) { if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print. // Move to the origin position for the copy we're going to print.
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
@ -1547,7 +1547,7 @@ void GCode::process_layer(
if (m_last_obj_copy != this_object_copy) if (m_last_obj_copy != this_object_copy)
m_avoid_crossing_perimeters.use_external_mp_once = true; m_avoid_crossing_perimeters.use_external_mp_once = true;
m_last_obj_copy = this_object_copy; m_last_obj_copy = this_object_copy;
this->set_origin(unscale(copy(0)), unscale(copy(1))); this->set_origin(unscale(copy));
if (object_by_extruder.support != nullptr && !print_wipe_extrusions) { if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
m_layer = layers[layer_id].support_layer; m_layer = layers[layer_id].support_layer;
gcode += this->extrude_support( gcode += this->extrude_support(
@ -1632,7 +1632,7 @@ void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
} }
} }
void GCode::set_origin(const Pointf &pointf) void GCode::set_origin(const Vec2d &pointf)
{ {
// if origin increases (goes towards right), last_pos decreases because it goes towards left // if origin increases (goes towards right), last_pos decreases because it goes towards left
const Point translate( const Point translate(
@ -2618,24 +2618,21 @@ std::string GCode::set_extruder(unsigned int extruder_id)
} }
// convert a model-space scaled point into G-code coordinates // convert a model-space scaled point into G-code coordinates
Pointf GCode::point_to_gcode(const Point &point) const Vec2d GCode::point_to_gcode(const Point &point) const
{ {
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Pointf( return unscale(point) + m_origin - extruder_offset;
unscale(point(0)) + m_origin(0) - extruder_offset(0),
unscale(point(1)) + m_origin(1) - extruder_offset(1));
} }
// convert a model-space scaled point into G-code coordinates // convert a model-space scaled point into G-code coordinates
Point GCode::gcode_to_point(const Pointf &point) const Point GCode::gcode_to_point(const Vec2d &point) const
{ {
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Point( return Point(
scale_(point(0) - m_origin(0) + extruder_offset(0)), scale_(point(0) - m_origin(0) + extruder_offset(0)),
scale_(point(1) - m_origin(1) + extruder_offset(1))); scale_(point(1) - m_origin(1) + extruder_offset(1)));
} }
// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
// during infill/perimeter wiping, or normally (depends on wiping_entities parameter) // during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
// Returns a reference to member to avoid copying. // Returns a reference to member to avoid copying.

View file

@ -125,6 +125,7 @@ private:
class GCode { class GCode {
public: public:
GCode() : GCode() :
m_origin(Vec2d::Zero()),
m_enable_loop_clipping(true), m_enable_loop_clipping(true),
m_enable_cooling_markers(false), m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false), m_enable_extrusion_role_markers(false),
@ -152,12 +153,12 @@ public:
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr); void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Pointf& origin() const { return m_origin; } const Vec2d& origin() const { return m_origin; }
void set_origin(const Pointf &pointf); void set_origin(const Vec2d &pointf);
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); } void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); }
const Point& last_pos() const { return m_last_pos; } const Point& last_pos() const { return m_last_pos; }
Pointf point_to_gcode(const Point &point) const; Vec2d point_to_gcode(const Point &point) const;
Point gcode_to_point(const Pointf &point) const; Point gcode_to_point(const Vec2d &point) const;
const FullPrintConfig &config() const { return m_config; } const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; } GCodeWriter& writer() { return m_writer; }
@ -258,7 +259,7 @@ protected:
/* Origin of print coordinates expressed in unscaled G-code coordinates. /* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to() This affects the input arguments supplied to the extrude*() and travel_to()
methods. */ methods. */
Pointf m_origin; Vec2d m_origin;
FullPrintConfig m_config; FullPrintConfig m_config;
GCodeWriter m_writer; GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser; PlaceholderParser m_placeholder_parser;

View file

@ -14,7 +14,7 @@ static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float INCHES_TO_MM = 25.4f; static const float INCHES_TO_MM = 25.4f;
static const float DEFAULT_FEEDRATE = 0.0f; static const float DEFAULT_FEEDRATE = 0.0f;
static const unsigned int DEFAULT_EXTRUDER_ID = 0; static const unsigned int DEFAULT_EXTRUDER_ID = 0;
static const Slic3r::Pointf3 DEFAULT_START_POSITION = Slic3r::Pointf3(0.0f, 0.0f, 0.0f); static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f);
static const float DEFAULT_START_EXTRUSION = 0.0f; static const float DEFAULT_START_EXTRUSION = 0.0f;
namespace Slic3r { namespace Slic3r {
@ -71,7 +71,7 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other)
return false; return false;
} }
GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder)
: type(type) : type(type)
, data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate) , data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate)
, start_position(start_position) , start_position(start_position)
@ -80,7 +80,7 @@ GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusi
{ {
} }
GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder) GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder)
: type(type) : type(type)
, data(data) , data(data)
, start_position(start_position) , start_position(start_position)
@ -587,12 +587,12 @@ void GCodeAnalyzer::_reset_axes_position()
::memset((void*)m_state.position, 0, Num_Axis * sizeof(float)); ::memset((void*)m_state.position, 0, Num_Axis * sizeof(float));
} }
void GCodeAnalyzer::_set_start_position(const Pointf3& position) void GCodeAnalyzer::_set_start_position(const Vec3d& position)
{ {
m_state.start_position = position; m_state.start_position = position;
} }
const Pointf3& GCodeAnalyzer::_get_start_position() const const Vec3d& GCodeAnalyzer::_get_start_position() const
{ {
return m_state.start_position; return m_state.start_position;
} }
@ -612,9 +612,9 @@ float GCodeAnalyzer::_get_delta_extrusion() const
return _get_axis_position(E) - m_state.start_extrusion; return _get_axis_position(E) - m_state.start_extrusion;
} }
Pointf3 GCodeAnalyzer::_get_end_position() const Vec3d GCodeAnalyzer::_get_end_position() const
{ {
return Pointf3(m_state.position[X], m_state.position[Y], m_state.position[Z]); return Vec3d(m_state.position[X], m_state.position[Y], m_state.position[Z]);
} }
void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type) void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
@ -673,7 +673,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
Metadata data; Metadata data;
float z = FLT_MAX; float z = FLT_MAX;
Polyline polyline; Polyline polyline;
Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX);
float volumetric_rate = FLT_MAX; float volumetric_rate = FLT_MAX;
GCodePreviewData::Range height_range; GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range; GCodePreviewData::Range width_range;
@ -742,7 +742,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
return; return;
Polyline3 polyline; Polyline3 polyline;
Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX);
GCodePreviewData::Travel::EType type = GCodePreviewData::Travel::Num_Types; GCodePreviewData::Travel::EType type = GCodePreviewData::Travel::Num_Types;
GCodePreviewData::Travel::Polyline::EDirection direction = GCodePreviewData::Travel::Polyline::Num_Directions; GCodePreviewData::Travel::Polyline::EDirection direction = GCodePreviewData::Travel::Polyline::Num_Directions;
float feedrate = FLT_MAX; float feedrate = FLT_MAX;
@ -768,12 +768,12 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
polyline = Polyline3(); polyline = Polyline3();
// add both vertices of the move // add both vertices of the move
polyline.append(Point3(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()))); polyline.append(Vec3crd(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())));
polyline.append(Point3(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z()))); polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z())));
} }
else else
// append end vertex of the move to current polyline // append end vertex of the move to current polyline
polyline.append(Point3(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z()))); polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z())));
// update current values // update current values
position = move.end_position; position = move.end_position;
@ -804,7 +804,7 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da
for (const GCodeMove& move : retraction_moves->second) for (const GCodeMove& move : retraction_moves->second)
{ {
// store position // store position
Point3 position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height); preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height);
} }
} }
@ -818,7 +818,7 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
for (const GCodeMove& move : unretraction_moves->second) for (const GCodeMove& move : unretraction_moves->second)
{ {
// store position // store position
Point3 position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())); Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height); preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height);
} }
} }

View file

@ -75,12 +75,12 @@ public:
EType type; EType type;
Metadata data; Metadata data;
Pointf3 start_position; Vec3d start_position;
Pointf3 end_position; Vec3d end_position;
float delta_extruder; float delta_extruder;
GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
GCodeMove(EType type, const Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder); GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
}; };
typedef std::vector<GCodeMove> GCodeMovesList; typedef std::vector<GCodeMove> GCodeMovesList;
@ -93,7 +93,7 @@ private:
EPositioningType global_positioning_type; EPositioningType global_positioning_type;
EPositioningType e_local_positioning_type; EPositioningType e_local_positioning_type;
Metadata data; Metadata data;
Pointf3 start_position; Vec3d start_position = Vec3d::Zero();
float start_extrusion; float start_extrusion;
float position[Num_Axis]; float position[Num_Axis];
}; };
@ -206,15 +206,15 @@ private:
// Sets axes position to zero // Sets axes position to zero
void _reset_axes_position(); void _reset_axes_position();
void _set_start_position(const Pointf3& position); void _set_start_position(const Vec3d& position);
const Pointf3& _get_start_position() const; const Vec3d& _get_start_position() const;
void _set_start_extrusion(float extrusion); void _set_start_extrusion(float extrusion);
float _get_start_extrusion() const; float _get_start_extrusion() const;
float _get_delta_extrusion() const; float _get_delta_extrusion() const;
// Returns current xyz position (from m_state.position[]) // Returns current xyz position (from m_state.position[])
Pointf3 _get_end_position() const; Vec3d _get_end_position() const;
// Adds a new move with the given data // Adds a new move with the given data
void _store_move(GCodeMove::EType type); void _store_move(GCodeMove::EType type);

View file

@ -23,7 +23,7 @@ CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_
void CoolingBuffer::reset() void CoolingBuffer::reset()
{ {
m_current_pos.assign(5, 0.f); m_current_pos.assign(5, 0.f);
Pointf3 pos = m_gcodegen.writer().get_position(); Vec3d pos = m_gcodegen.writer().get_position();
m_current_pos[0] = float(pos(0)); m_current_pos[0] = float(pos(0));
m_current_pos[1] = float(pos(1)); m_current_pos[1] = float(pos(1));
m_current_pos[2] = float(pos(2)); m_current_pos[2] = float(pos(2));

View file

@ -226,7 +226,7 @@ void GCodePreviewData::Travel::set_default()
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f); const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
GCodePreviewData::Retraction::Position::Position(const Point3& position, float width, float height) GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
: position(position) : position(position)
, width(width) , width(width)
, height(height) , height(height)

View file

@ -151,11 +151,11 @@ public:
struct Position struct Position
{ {
Point3 position; Vec3crd position;
float width; float width;
float height; float height;
Position(const Point3& position, float width, float height); Position(const Vec3crd& position, float width, float height);
}; };
typedef std::vector<Position> PositionsList; typedef std::vector<Position> PositionsList;

View file

@ -32,8 +32,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusio
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)); BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = Pointf::new_unscale(bbox.min); bboxf.min = unscale(bbox.min);
bboxf.max = Pointf::new_unscale(bbox.max); bboxf.max = unscale(bbox.max);
bboxf.defined = true; bboxf.defined = true;
} }
return bboxf; return bboxf;
@ -46,8 +46,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = Pointf::new_unscale(bbox.min); bboxf.min = unscale(bbox.min);
bboxf.max = Pointf::new_unscale(bbox.max); bboxf.max = unscale(bbox.max);
bboxf.defined = true; bboxf.defined = true;
} }
return bboxf; return bboxf;
@ -60,8 +60,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = Pointf::new_unscale(bbox.min); bboxf.min = unscale(bbox.min);
bboxf.max = Pointf::new_unscale(bbox.max); bboxf.max = unscale(bbox.max);
bboxf.defined = true; bboxf.defined = true;
} }
return bboxf; return bboxf;
@ -123,7 +123,7 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
bbox_this.merge(extrusionentity_extents(extrusion_entity)); bbox_this.merge(extrusionentity_extents(extrusion_entity));
for (const Point &offset : print_object._shifted_copies) { for (const Point &offset : print_object._shifted_copies) {
BoundingBoxf bbox_translated(bbox_this); BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(Pointf::new_unscale(offset)); bbox_translated.translate(unscale(offset));
bbox.merge(bbox_translated); bbox.merge(bbox_translated);
} }
} }
@ -136,8 +136,9 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
{ {
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation // Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position. // We need to get position and angle of the wipe tower to transform them to actual position.
Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value); Transform2d trafo =
float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value; Eigen::Translation2d(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config.wipe_tower_rotation_angle.value);
BoundingBoxf bbox; BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) { for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
@ -147,19 +148,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i]; const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) { if (e.width > 0) {
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); Vec2d delta = 0.5 * Vec2d(e.width, e.width);
Pointf p2(e.pos.x, e.pos.y); Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
p1.rotate(wipe_tower_angle); Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
p1 += wipe_tower_pos; bbox.merge(p1.cwiseMin(p2) - delta);
p2.rotate(wipe_tower_angle); bbox.merge(p1.cwiseMax(p2) + delta);
p2 += wipe_tower_pos;
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
} }
} }
} }
@ -176,8 +169,8 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i]; const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) { if (e.width > 0) {
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y); Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y);
Pointf p2(e.pos.x, e.pos.y); Vec2d p2(e.pos.x, e.pos.y);
bbox.merge(p1); bbox.merge(p1);
coordf_t radius = 0.5 * e.width; coordf_t radius = 0.5 * e.width;
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius); bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);

View file

@ -276,7 +276,7 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
return gcode.str(); return gcode.str();
} }
std::string GCodeWriter::travel_to_xy(const Pointf &point, const std::string &comment) std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
{ {
m_pos(0) = point(0); m_pos(0) = point(0);
m_pos(1) = point(1); m_pos(1) = point(1);
@ -290,7 +290,7 @@ std::string GCodeWriter::travel_to_xy(const Pointf &point, const std::string &co
return gcode.str(); return gcode.str();
} }
std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string &comment) std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
{ {
/* If target Z is lower than current Z but higher than nominal Z we /* If target Z is lower than current Z but higher than nominal Z we
don't perform the Z move but we only move in the XY plane and don't perform the Z move but we only move in the XY plane and
@ -299,7 +299,7 @@ std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string &
if (!this->will_move_z(point(2))) { if (!this->will_move_z(point(2))) {
double nominal_z = m_pos(2) - m_lifted; double nominal_z = m_pos(2) - m_lifted;
m_lifted = m_lifted - (point(2) - nominal_z); m_lifted = m_lifted - (point(2) - nominal_z);
return this->travel_to_xy(point.xy()); return this->travel_to_xy(to_2d(point));
} }
/* In all the other cases, we perform an actual XYZ move and cancel /* In all the other cases, we perform an actual XYZ move and cancel
@ -358,7 +358,7 @@ bool GCodeWriter::will_move_z(double z) const
return true; return true;
} }
std::string GCodeWriter::extrude_to_xy(const Pointf &point, double dE, const std::string &comment) std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment)
{ {
m_pos(0) = point(0); m_pos(0) = point(0);
m_pos(1) = point(1); m_pos(1) = point(1);
@ -373,7 +373,7 @@ std::string GCodeWriter::extrude_to_xy(const Pointf &point, double dE, const std
return gcode.str(); return gcode.str();
} }
std::string GCodeWriter::extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment) std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
{ {
m_pos = point; m_pos = point;
m_lifted = 0; m_lifted = 0;

View file

@ -55,18 +55,18 @@ public:
std::string toolchange_prefix() const; std::string toolchange_prefix() const;
std::string toolchange(unsigned int extruder_id); std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string()); std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string());
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string()); std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string());
std::string travel_to_z(double z, const std::string &comment = std::string()); std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const; bool will_move_z(double z) const;
std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string());
std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string());
std::string retract(bool before_wipe = false); std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract(); std::string unretract();
std::string lift(); std::string lift();
std::string unlift(); std::string unlift();
Pointf3 get_position() const { return m_pos; } Vec3d get_position() const { return m_pos; }
private: private:
std::vector<Extruder> m_extruders; std::vector<Extruder> m_extruders;
@ -81,7 +81,7 @@ private:
unsigned int m_last_bed_temperature; unsigned int m_last_bed_temperature;
bool m_last_bed_temperature_reached; bool m_last_bed_temperature_reached;
double m_lifted; double m_lifted;
Pointf3 m_pos; Vec3d m_pos = Vec3d::Zero();
std::string _travel_to_z(double z, const std::string &comment); std::string _travel_to_z(double z, const std::string &comment);
std::string _retract(double length, double restart_extra, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment);

View file

@ -345,7 +345,7 @@ linint(double value, double oldmin, double oldmax, double newmin, double newmax)
// If the points have the same weight, sort them lexicographically by their positions. // If the points have the same weight, sort them lexicographically by their positions.
struct ArrangeItem { struct ArrangeItem {
ArrangeItem() {} ArrangeItem() {}
Pointf pos; Vec2d pos;
coordf_t weight; coordf_t weight;
bool operator<(const ArrangeItem &other) const { bool operator<(const ArrangeItem &other) const {
return weight < other.weight || return weight < other.weight ||
@ -353,17 +353,17 @@ struct ArrangeItem {
} }
}; };
Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box) Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box)
{ {
// Use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm. // Use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm.
const Pointf cell_size(part_size(0) + gap, part_size(1) + gap); const Vec2d cell_size(part_size(0) + gap, part_size(1) + gap);
const BoundingBoxf bed_bbox = (bed_bounding_box != NULL && bed_bounding_box->defined) ? const BoundingBoxf bed_bbox = (bed_bounding_box != NULL && bed_bounding_box->defined) ?
*bed_bounding_box : *bed_bounding_box :
// Bogus bed size, large enough not to trigger the unsufficient bed size error. // Bogus bed size, large enough not to trigger the unsufficient bed size error.
BoundingBoxf( BoundingBoxf(
Pointf(0, 0), Vec2d(0, 0),
Pointf(cell_size(0) * num_parts, cell_size(1) * num_parts)); Vec2d(cell_size(0) * num_parts, cell_size(1) * num_parts));
// This is how many cells we have available into which to put parts. // This is how many cells we have available into which to put parts.
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0))); size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
@ -372,8 +372,8 @@ Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const B
CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts); CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed. // Get a bounding box of cellw x cellh cells, centered at the center of the bed.
Pointf cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap); Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);
Pointf cells_offset(bed_bbox.center() - 0.5 * cells_size); Vec2d cells_offset(bed_bbox.center() - 0.5 * cells_size);
BoundingBoxf cells_bb(cells_offset, cells_size + cells_offset); BoundingBoxf cells_bb(cells_offset, cells_size + cells_offset);
// List of cells, sorted by distance from center. // List of cells, sorted by distance from center.
@ -405,35 +405,35 @@ Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const B
Pointfs positions; Pointfs positions;
positions.reserve(num_parts); positions.reserve(num_parts);
for (std::vector<ArrangeItem>::const_iterator it = cellsorder.begin(); it != cellsorder.end(); ++ it) for (std::vector<ArrangeItem>::const_iterator it = cellsorder.begin(); it != cellsorder.end(); ++ it)
positions.push_back(Pointf(it->pos(0) - 0.5 * part_size(0), it->pos(1) - 0.5 * part_size(1))); positions.push_back(Vec2d(it->pos(0) - 0.5 * part_size(0), it->pos(1) - 0.5 * part_size(1)));
return positions; return positions;
} }
#else #else
class ArrangeItem { class ArrangeItem {
public: public:
Pointf pos; Vec2d pos = Vec2d::Zero();
size_t index_x, index_y; size_t index_x, index_y;
coordf_t dist; coordf_t dist;
}; };
class ArrangeItemIndex { class ArrangeItemIndex {
public: public:
coordf_t index; coordf_t index;
ArrangeItem item; ArrangeItem item;
ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {}; ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {};
}; };
bool bool
arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions) arrange(size_t total_parts, const Vec2d &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions)
{ {
positions.clear(); positions.clear();
Pointf part = part_size; Vec2d part = part_size;
// use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm // use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm
part(0) += dist; part(0) += dist;
part(1) += dist; part(1) += dist;
Pointf area; Vec2d area(Vec2d::Zero());
if (bb != NULL && bb->defined) { if (bb != NULL && bb->defined) {
area = bb->size(); area = bb->size();
} else { } else {
@ -449,11 +449,11 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi
return false; return false;
// total space used by cells // total space used by cells
Pointf cells(cellw * part(0), cellh * part(1)); Vec2d cells(cellw * part(0), cellh * part(1));
// bounding box of total space used by cells // bounding box of total space used by cells
BoundingBoxf cells_bb; BoundingBoxf cells_bb;
cells_bb.merge(Pointf(0,0)); // min cells_bb.merge(Vec2d(0,0)); // min
cells_bb.merge(cells); // max cells_bb.merge(cells); // max
// center bounding box to area // center bounding box to area
@ -533,7 +533,7 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi
coordf_t cx = c.item.index_x - lx; coordf_t cx = c.item.index_x - lx;
coordf_t cy = c.item.index_y - ty; coordf_t cy = c.item.index_y - ty;
positions.push_back(Pointf(cx * part(0), cy * part(1))); positions.push_back(Vec2d(cx * part(0), cy * part(1)));
} }
if (bb != NULL && bb->defined) { if (bb != NULL && bb->defined) {

View file

@ -66,7 +66,7 @@ static inline bool is_ccw(const Polygon &poly)
return o == ORIENTATION_CCW; return o == ORIENTATION_CCW;
} }
inline bool ray_ray_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res) inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
{ {
double denom = v1(0) * v2(1) - v2(0) * v1(1); double denom = v1(0) * v2(1) - v2(0) * v1(1);
if (std::abs(denom) < EPSILON) if (std::abs(denom) < EPSILON)
@ -77,7 +77,7 @@ inline bool ray_ray_intersection(const Pointf &p1, const Vectorf &v1, const Poin
return true; return true;
} }
inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res) inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
{ {
double denom = v1(0) * v2(1) - v2(0) * v1(1); double denom = v1(0) * v2(1) - v2(0) * v1(1);
if (std::abs(denom) < EPSILON) if (std::abs(denom) < EPSILON)
@ -123,7 +123,7 @@ void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* ret
double linint(double value, double oldmin, double oldmax, double newmin, double newmax); double linint(double value, double oldmin, double oldmax, double newmin, double newmax);
bool arrange( bool arrange(
// input // input
size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box, size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box,
// output // output
Pointfs &positions); Pointfs &positions);

View file

@ -97,11 +97,11 @@ bool Line::intersection(const Line &l2, Point *intersection) const
return false; // not intersecting return false; // not intersecting
} }
Pointf3 Linef3::intersect_plane(double z) const Vec3d Linef3::intersect_plane(double z) const
{ {
auto v = (this->b - this->a).cast<double>(); auto v = (this->b - this->a).cast<double>();
double t = (z - this->a(2)) / v(2); double t = (z - this->a(2)) / v(2);
return Pointf3(this->a(0) + v(0) * t, this->a(1) + v(1) * t, z); return Vec3d(this->a(0) + v(0) * t, this->a(1) + v(1) * t, z);
} }
} }

View file

@ -19,7 +19,7 @@ class Line
{ {
public: public:
Line() {} Line() {}
explicit Line(Point _a, Point _b): a(_a), b(_b) {} Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
void scale(double factor) { this->a *= factor; this->b *= factor; } void scale(double factor) { this->a *= factor; this->b *= factor; }
void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; } void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; }
@ -49,45 +49,49 @@ class ThickLine : public Line
{ {
public: public:
ThickLine() : a_width(0), b_width(0) {} ThickLine() : a_width(0), b_width(0) {}
ThickLine(Point a, Point b) : Line(a, b), a_width(0), b_width(0) {} ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {}
ThickLine(Point a, Point b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {} ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {}
coordf_t a_width, b_width; double a_width, b_width;
}; };
class Line3 class Line3
{ {
public: public:
Line3() {} Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {}
Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {} Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {}
double length() const { return (this->a - this->b).cast<double>().norm(); } double length() const { return (this->a - this->b).cast<double>().norm(); }
Vector3 vector() const { return this->b - this->a; } Vec3crd vector() const { return this->b - this->a; }
Point3 a; Vec3crd a;
Point3 b; Vec3crd b;
}; };
class Linef class Linef
{ {
public: public:
Linef() {} Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
explicit Linef(Pointf _a, Pointf _b): a(_a), b(_b) {} Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
Pointf a; Vec2d a;
Pointf b; Vec2d b;
}; };
class Linef3 class Linef3
{ {
public: public:
Linef3() {} Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {}
explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {} Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {}
Pointf3 intersect_plane(double z) const;
void scale(double factor) { this->a *= factor; this->b *= factor; }
Pointf3 a; Vec3d intersect_plane(double z) const;
Pointf3 b; void scale(double factor) { this->a *= factor; this->b *= factor; }
Vec3d vector() const { return this->b - this->a; }
Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); }
double length() const { return vector().norm(); }
Vec3d a;
Vec3d b;
}; };
} // namespace Slic3r } // namespace Slic3r

View file

@ -235,15 +235,7 @@ BoundingBoxf3 Model::bounding_box() const
return bb; return bb;
} }
BoundingBoxf3 Model::transformed_bounding_box() const void Model::center_instances_around_point(const Vec2d &point)
{
BoundingBoxf3 bb;
for (const ModelObject* obj : this->objects)
bb.merge(obj->tight_bounding_box(false));
return bb;
}
void Model::center_instances_around_point(const Pointf &point)
{ {
// BoundingBoxf3 bb = this->bounding_box(); // BoundingBoxf3 bb = this->bounding_box();
BoundingBoxf3 bb; BoundingBoxf3 bb;
@ -251,7 +243,7 @@ void Model::center_instances_around_point(const Pointf &point)
for (size_t i = 0; i < o->instances.size(); ++ i) for (size_t i = 0; i < o->instances.size(); ++ i)
bb.merge(o->instance_bounding_box(i, false)); bb.merge(o->instance_bounding_box(i, false));
Pointf shift = point - 0.5 * bb.size().xy() - bb.min.xy(); Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
for (ModelObject *o : this->objects) { for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) for (ModelInstance *i : o->instances)
i->offset += shift; i->offset += shift;
@ -309,8 +301,8 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
for (size_t i = 0; i < o->instances.size(); ++ i) { for (size_t i = 0; i < o->instances.size(); ++ i) {
// an accurate snug bounding box around the transformed mesh. // an accurate snug bounding box around the transformed mesh.
BoundingBoxf3 bbox(o->instance_bounding_box(i, true)); BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
instance_sizes.push_back(bbox.size().xy()); instance_sizes.emplace_back(to_2d(bbox.size()));
instance_centers.push_back(bbox.center().xy()); instance_centers.emplace_back(to_2d(bbox.center()));
} }
Pointfs positions; Pointfs positions;
@ -332,7 +324,7 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
// Duplicate the entire model preserving instance relative positions. // Duplicate the entire model preserving instance relative positions.
void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
{ {
Pointfs model_sizes(copies_num-1, this->bounding_box().size().xy()); Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size()));
Pointfs positions; Pointfs positions;
if (! _arrange(model_sizes, dist, bb, positions)) if (! _arrange(model_sizes, dist, bb, positions))
CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
@ -343,7 +335,7 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
// make a copy of the pointers in order to avoid recursion when appending their copies // make a copy of the pointers in order to avoid recursion when appending their copies
ModelInstancePtrs instances = o->instances; ModelInstancePtrs instances = o->instances;
for (const ModelInstance *i : instances) { for (const ModelInstance *i : instances) {
for (const Pointf &pos : positions) { for (const Vec2d &pos : positions) {
ModelInstance *instance = o->add_instance(*i); ModelInstance *instance = o->add_instance(*i);
instance->offset += pos; instance->offset += pos;
} }
@ -375,7 +367,7 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
ModelObject* object = this->objects.front(); ModelObject* object = this->objects.front();
object->clear_instances(); object->clear_instances();
Sizef3 size = object->bounding_box().size(); Vec3d size = object->bounding_box().size();
for (size_t x_copy = 1; x_copy <= x; ++x_copy) { for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
for (size_t y_copy = 1; y_copy <= y; ++y_copy) { for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
@ -621,54 +613,6 @@ const BoundingBoxf3& ModelObject::bounding_box() const
return m_bounding_box; return m_bounding_box;
} }
BoundingBoxf3 ModelObject::tight_bounding_box(bool include_modifiers) const
{
BoundingBoxf3 bb;
for (const ModelVolume* vol : this->volumes)
{
if (include_modifiers || !vol->modifier)
{
for (const ModelInstance* inst : this->instances)
{
double c = cos(inst->rotation);
double s = sin(inst->rotation);
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet& facet = vol->mesh.stl.facet_start[f];
for (int i = 0; i < 3; ++i)
{
// original point
const stl_vertex& v = facet.vertex[i];
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
// scale
p(0) *= inst->scaling_factor;
p(1) *= inst->scaling_factor;
p(2) *= inst->scaling_factor;
// rotate Z
double x = p(0);
double y = p(1);
p(0) = c * x - s * y;
p(1) = s * x + c * y;
// translate
p(0) += inst->offset(0);
p(1) += inst->offset(1);
bb.merge(p);
}
}
}
}
}
return bb;
}
// A mesh containing all transformed instances of this object. // A mesh containing all transformed instances of this object.
TriangleMesh ModelObject::mesh() const TriangleMesh ModelObject::mesh() const
{ {
@ -726,24 +670,22 @@ void ModelObject::center_around_origin()
if (! v->modifier) if (! v->modifier)
bb.merge(v->mesh.bounding_box()); bb.merge(v->mesh.bounding_box());
// first align to origin on XYZ // First align to origin on XYZ, then center it on XY.
Vectorf3 vector(-bb.min(0), -bb.min(1), -bb.min(2)); Vec3d size = bb.size();
size(2) = 0.;
Vec3d shift3 = - bb.min - 0.5 * size;
// Unaligned vector, for the Rotation2D to work on Visual Studio 2013.
Eigen::Vector2d shift2 = to_2d(shift3);
// then center it on XY this->translate(shift3);
Sizef3 size = bb.size(); this->origin_translation += shift3;
vector(0) -= size(0)/2;
vector(1) -= size(1)/2;
this->translate(vector);
this->origin_translation += vector;
if (!this->instances.empty()) { if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) { for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance, // apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered // in order to leave final position unaltered
Vectorf v = - vector.xy(); Eigen::Rotation2Dd rot(i->rotation);
v.rotate(i->rotation); i->offset -= rot * shift2 * i->scaling_factor;
i->offset += v * i->scaling_factor;
} }
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
@ -752,39 +694,38 @@ void ModelObject::center_around_origin()
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z) void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
{ {
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{
v->mesh.translate(float(x), float(y), float(z)); v->mesh.translate(float(x), float(y), float(z));
v->m_convex_hull.translate(float(x), float(y), float(z));
}
if (m_bounding_box_valid) if (m_bounding_box_valid)
m_bounding_box.translate(x, y, z); m_bounding_box.translate(x, y, z);
} }
void ModelObject::scale(const Pointf3 &versor) void ModelObject::scale(const Vec3d &versor)
{ {
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{
v->mesh.scale(versor); v->mesh.scale(versor);
v->m_convex_hull.scale(versor);
}
// reset origin translation since it doesn't make sense anymore // reset origin translation since it doesn't make sense anymore
this->origin_translation = Pointf3(0,0,0); this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void ModelObject::rotate(float angle, const Axis &axis) void ModelObject::rotate(float angle, const Axis &axis)
{ {
float min_z = FLT_MAX;
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{ {
v->mesh.rotate(angle, axis); v->mesh.rotate(angle, axis);
min_z = std::min(min_z, v->mesh.stl.stats.min.z); v->m_convex_hull.rotate(angle, axis);
} }
if (min_z != 0.0f) center_around_origin();
{
// translate the object so that its minimum z lays on the bed
for (ModelVolume *v : this->volumes)
{
v->mesh.translate(0.0f, 0.0f, -min_z);
}
}
this->origin_translation = Pointf3(0, 0, 0); this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
@ -796,17 +737,22 @@ void ModelObject::transform(const float* matrix3x4)
for (ModelVolume* v : volumes) for (ModelVolume* v : volumes)
{ {
v->mesh.transform(matrix3x4); v->mesh.transform(matrix3x4);
v->m_convex_hull.transform(matrix3x4);
} }
origin_translation = Pointf3(0.0, 0.0, 0.0); this->origin_translation = Vec3d::Zero();
invalidate_bounding_box(); this->invalidate_bounding_box();
} }
void ModelObject::mirror(const Axis &axis) void ModelObject::mirror(const Axis &axis)
{ {
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{
v->mesh.mirror(axis); v->mesh.mirror(axis);
this->origin_translation = Pointf3(0,0,0); v->m_convex_hull.mirror(axis);
}
this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
@ -910,45 +856,18 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{ {
for (ModelVolume* vol : this->volumes) for (const ModelVolume* vol : this->volumes)
{ {
if (!vol->modifier) if (!vol->modifier)
{ {
for (ModelInstance* inst : this->instances) for (ModelInstance* inst : this->instances)
{ {
BoundingBoxf3 bb; Transform3d m = Transform3d::Identity();
m.translate(Vec3d(inst->offset(0), inst->offset(1), 0.0));
m.rotate(Eigen::AngleAxisd(inst->rotation, Vec3d::UnitZ()));
m.scale(inst->scaling_factor);
double c = cos(inst->rotation); BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(m);
double s = sin(inst->rotation);
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet& facet = vol->mesh.stl.facet_start[f];
for (int i = 0; i < 3; ++i)
{
// original point
const stl_vertex& v = facet.vertex[i];
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
// scale
p(0) *= inst->scaling_factor;
p(1) *= inst->scaling_factor;
p(2) *= inst->scaling_factor;
// rotate Z
double x = p(0);
double y = p(1);
p(0) = c * x - s * y;
p(1) = s * x + c * y;
// translate
p(0) += inst->offset(0);
p(1) += inst->offset(1);
bb.merge(p);
}
}
if (print_volume.contains(bb)) if (print_volume.contains(bb))
inst->print_volume_state = ModelInstance::PVS_Inside; inst->print_volume_state = ModelInstance::PVS_Inside;
@ -970,7 +889,7 @@ void ModelObject::print_info() const
TriangleMesh mesh = this->raw_mesh(); TriangleMesh mesh = this->raw_mesh();
mesh.check_topology(); mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box(); BoundingBoxf3 bb = mesh.bounding_box();
Sizef3 size = bb.size(); Vec3d size = bb.size();
cout << "size_x = " << size(0) << endl; cout << "size_x = " << size(0) << endl;
cout << "size_y = " << size(1) << endl; cout << "size_y = " << size(1) << endl;
cout << "size_z = " << size(2) << endl; cout << "size_z = " << size(2) << endl;
@ -1031,6 +950,16 @@ ModelMaterial* ModelVolume::assign_unique_material()
return model->add_material(this->_material_id); return model->add_material(this->_material_id);
} }
void ModelVolume::calculate_convex_hull()
{
m_convex_hull = mesh.convex_hull_3d();
}
const TriangleMesh& ModelVolume::get_convex_hull() const
{
return m_convex_hull;
}
// Split this volume, append the result to the object owning this volume. // Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one. // Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object. // This is useful to assign different materials to different volumes of an object.
@ -1082,30 +1011,20 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) { for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) {
const stl_facet &facet = mesh->stl.facet_start[i]; const stl_facet &facet = mesh->stl.facet_start[i];
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < 3; ++ j) {
stl_vertex v = facet.vertex[j]; const stl_vertex &v = facet.vertex[j];
double xold = v.x; bbox.merge(Vec3d(c * v(0) - s * v(1), s * v(0) + c * v(1), v(2)));
double yold = v.y;
v.x = float(c * xold - s * yold);
v.y = float(s * xold + c * yold);
bbox.merge(Pointf3(v.x, v.y, v.z));
} }
} }
if (! empty(bbox)) { if (! empty(bbox)) {
// Scale the bounding box uniformly. // Scale the bounding box uniformly.
if (std::abs(this->scaling_factor - 1.) > EPSILON) { if (std::abs(this->scaling_factor - 1.) > EPSILON) {
bbox.min(0) *= float(this->scaling_factor); bbox.min *= this->scaling_factor;
bbox.min(1) *= float(this->scaling_factor); bbox.max *= this->scaling_factor;
bbox.min(2) *= float(this->scaling_factor);
bbox.max(0) *= float(this->scaling_factor);
bbox.max(1) *= float(this->scaling_factor);
bbox.max(2) *= float(this->scaling_factor);
} }
// Translate the bounding box. // Translate the bounding box.
if (! dont_translate) { if (! dont_translate) {
bbox.min(0) += float(this->offset(0)); Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
bbox.min(1) += float(this->offset(1)); Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
bbox.max(0) += float(this->offset(0));
bbox.max(1) += float(this->offset(1));
} }
} }
return bbox; return bbox;
@ -1113,10 +1032,11 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{ {
auto matrix = Transform3f::Identity(); Transform3d matrix = Transform3d::Identity();
if (!dont_translate) if (!dont_translate)
matrix.translate(Vec3f((float)offset(0), (float)offset(1), 0.0f)); matrix.translate(Vec3d(offset(0), offset(1), 0.0));
matrix.rotate(Eigen::AngleAxisf(rotation, Vec3f::UnitZ()));
matrix.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
matrix.scale(scaling_factor); matrix.scale(scaling_factor);
return bbox.transformed(matrix); return bbox.transformed(matrix);
} }

View file

@ -84,7 +84,7 @@ public:
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment
when user expects that. */ when user expects that. */
Pointf3 origin_translation; Vec3d origin_translation;
Model* get_model() const { return m_model; }; Model* get_model() const { return m_model; };
@ -105,9 +105,6 @@ public:
// This bounding box is being cached. // This bounding box is being cached.
const BoundingBoxf3& bounding_box() const; const BoundingBoxf3& bounding_box() const;
void invalidate_bounding_box() { m_bounding_box_valid = false; } void invalidate_bounding_box() { m_bounding_box_valid = false; }
// Returns a snug bounding box of the transformed instances.
// This bounding box is not being cached.
BoundingBoxf3 tight_bounding_box(bool include_modifiers) const;
// A mesh containing all transformed instances of this object. // A mesh containing all transformed instances of this object.
TriangleMesh mesh() const; TriangleMesh mesh() const;
@ -120,9 +117,9 @@ public:
// A snug bounding box around the transformed non-modifier object volumes. // A snug bounding box around the transformed non-modifier object volumes.
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const; BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
void center_around_origin(); void center_around_origin();
void translate(const Vectorf3 &vector) { this->translate(vector(0), vector(1), vector(2)); } void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
void translate(coordf_t x, coordf_t y, coordf_t z); void translate(coordf_t x, coordf_t y, coordf_t z);
void scale(const Pointf3 &versor); void scale(const Vec3d &versor);
void rotate(float angle, const Axis &axis); void rotate(float angle, const Axis &axis);
void transform(const float* matrix3x4); void transform(const float* matrix3x4);
void mirror(const Axis &axis); void mirror(const Axis &axis);
@ -138,7 +135,7 @@ public:
void print_info() const; void print_info() const;
private: private:
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), m_bounding_box_valid(false) {} ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true); ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true);
ModelObject& operator= (ModelObject other); ModelObject& operator= (ModelObject other);
void swap(ModelObject &other); void swap(ModelObject &other);
@ -157,6 +154,10 @@ private:
class ModelVolume class ModelVolume
{ {
friend class ModelObject; friend class ModelObject;
// The convex hull of this model's mesh.
TriangleMesh m_convex_hull;
public: public:
std::string name; std::string name;
// The triangular model. // The triangular model.
@ -180,19 +181,32 @@ public:
ModelMaterial* assign_unique_material(); ModelMaterial* assign_unique_material();
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
private: private:
// Parent object owning this ModelVolume. // Parent object owning this ModelVolume.
ModelObject* object; ModelObject* object;
t_model_material_id _material_id; t_model_material_id _material_id;
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) {} ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
ModelVolume(ModelObject *object, TriangleMesh &&mesh) : mesh(std::move(mesh)), modifier(false), object(object) {} {
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other) : ModelVolume(ModelObject *object, const ModelVolume &other) :
name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object) name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
{ this->material_id(other.material_id()); } {
this->material_id(other.material_id());
}
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object) name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
{ this->material_id(other.material_id()); } {
this->material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
}; };
// A single instance of a ModelObject. // A single instance of a ModelObject.
@ -213,7 +227,7 @@ public:
// Transform3d transform; // Transform3d transform;
double rotation; // Rotation around the Z axis, in radians around mesh center point double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor; double scaling_factor;
Pointf offset; // in unscaled coordinates Vec2d offset; // in unscaled coordinates
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
EPrintVolumeState print_volume_state; EPrintVolumeState print_volume_state;
@ -235,7 +249,7 @@ private:
// Parent object, owning this instance. // Parent object, owning this instance.
ModelObject* object; ModelObject* object;
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object), print_volume_state(PVS_Inside) {} ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) : ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {} rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
}; };
@ -286,9 +300,7 @@ public:
bool add_default_instances(); bool add_default_instances();
// Returns approximate axis aligned bounding box of this model // Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
// Returns tight axis aligned bounding box of this model void center_instances_around_point(const Vec2d &point);
BoundingBoxf3 transformed_bounding_box() const;
void center_instances_around_point(const Pointf &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const; TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);

View file

@ -468,7 +468,7 @@ void applyResult(
// appropriately // appropriately
auto off = item.translation(); auto off = item.translation();
Radians rot = item.rotation(); Radians rot = item.rotation();
Pointf foff(off.X*SCALING_FACTOR + batch_offset, Vec2d foff(off.X*SCALING_FACTOR + batch_offset,
off.Y*SCALING_FACTOR); off.Y*SCALING_FACTOR);
// write the tranformation data into the model instance // write the tranformation data into the model instance

View file

@ -196,7 +196,7 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
void MultiPoint3::translate(double x, double y) void MultiPoint3::translate(double x, double y)
{ {
for (Point3 &p : points) { for (Vec3crd &p : points) {
p(0) += x; p(0) += x;
p(1) += y; p(1) += y;
} }

View file

@ -85,7 +85,7 @@ class MultiPoint3
public: public:
Points3 points; Points3 points;
void append(const Point3& point) { this->points.push_back(point); } void append(const Vec3crd& point) { this->points.push_back(point); }
void translate(double x, double y); void translate(double x, double y);
void translate(const Point& vector); void translate(const Point& vector);

View file

@ -243,7 +243,7 @@ void PerimeterGenerator::process()
perimeter_spacing / 2; perimeter_spacing / 2;
// only apply infill overlap if we actually have one perimeter // only apply infill overlap if we actually have one perimeter
if (inset > 0) if (inset > 0)
inset -= scale_(this->config->get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2))); inset -= scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2)));
// simplify infill contours according to resolution // simplify infill contours according to resolution
Polygons pp; Polygons pp;
for (ExPolygon &ex : last) for (ExPolygon &ex : last)
@ -420,7 +420,7 @@ static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyli
path.polyline.append(line.b); path.polyline.append(line.b);
// Convert from spacing to extrusion width based on the extrusion model // Convert from spacing to extrusion width based on the extrusion model
// of a square extrusion ended with semi circles. // of a square extrusion ended with semi circles.
flow.width = unscale(w) + flow.height * (1. - 0.25 * PI); flow.width = unscale<float>(w) + flow.height * (1. - 0.25 * PI);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf(" filling %f gap\n", flow.width); printf(" filling %f gap\n", flow.width);
#endif #endif

View file

@ -148,33 +148,11 @@ Point Point::projection_onto(const Line &line) const
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b; return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
} }
std::ostream& operator<<(std::ostream &stm, const Pointf &pointf) std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
{ {
return stm << pointf(0) << "," << pointf(1); return stm << pointf(0) << "," << pointf(1);
} }
void Pointf::rotate(double angle)
{
double cur_x = (*this)(0);
double cur_y = (*this)(1);
double s = ::sin(angle);
double c = ::cos(angle);
(*this)(0) = c * cur_x - s * cur_y;
(*this)(1) = c * cur_y + s * cur_x;
}
void Pointf::rotate(double angle, const Pointf &center)
{
double cur_x = (*this)(0);
double cur_y = (*this)(1);
double s = ::sin(angle);
double c = ::cos(angle);
double dx = cur_x - center(0);
double dy = cur_y - center(1);
(*this)(0) = center(0) + c * dx - s * dy;
(*this)(1) = center(1) + c * dy + s * dx;
}
namespace int128 { namespace int128 {
int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3) int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3)

View file

@ -16,19 +16,7 @@ namespace Slic3r {
class Line; class Line;
class MultiPoint; class MultiPoint;
class Point; class Point;
class Point3; typedef Point Vector;
class Pointf;
class Pointf3;
typedef Point Vector;
typedef Point3 Vector3;
typedef Pointf Vectorf;
typedef Pointf3 Vectorf3;
typedef std::vector<Point> Points;
typedef std::vector<Point*> PointPtrs;
typedef std::vector<const Point*> PointConstPtrs;
typedef std::vector<Point3> Points3;
typedef std::vector<Pointf> Pointfs;
typedef std::vector<Pointf3> Pointf3s;
// Eigen types, to replace the Slic3r's own types in the future. // Eigen types, to replace the Slic3r's own types in the future.
// Vector types with a fixed point coordinate base type. // Vector types with a fixed point coordinate base type.
@ -43,16 +31,37 @@ typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d; typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d;
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d; typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
typedef std::vector<Point> Points;
typedef std::vector<Point*> PointPtrs;
typedef std::vector<const Point*> PointConstPtrs;
typedef std::vector<Vec3crd> Points3;
typedef std::vector<Vec2d> Pointfs;
typedef std::vector<Vec3d> Pointf3s;
typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f; typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f;
typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d; typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d;
typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f; typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f;
typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d; typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d;
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline coord_t cross2(const Vec2crd &v1, const Vec2crd &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline coord_t cross2(const Vec2crd &v1, const Vec2crd &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline Vec2crd to_2d(const Vec3crd &pt3) { return Vec2crd(pt3(0), pt3(1)); }
inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }
inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
inline Vec2d unscale(const Vec2d &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
inline Vec3d unscale(coord_t x, coord_t y, coord_t z) { return Vec3d(unscale<double>(x), unscale<double>(y), unscale<double>(z)); }
inline Vec3d unscale(const Vec3crd &pt) { return Vec3d(unscale<double>(pt(0)), unscale<double>(pt(1)), unscale<double>(pt(2))); }
inline Vec3d unscale(const Vec3d &pt) { return Vec3d(unscale<double>(pt(0)), unscale<double>(pt(1)), unscale<double>(pt(2))); }
inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; }
inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; }
inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; } inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; }
@ -210,81 +219,7 @@ private:
coord_t m_grid_log2; coord_t m_grid_log2;
}; };
class Point3 : public Vec3crd std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf);
{
public:
typedef coord_t coord_type;
explicit Point3() { (*this)(0) = (*this)(1) = (*this)(2) = 0; }
explicit Point3(coord_t x, coord_t y, coord_t z) { (*this)(0) = x; (*this)(1) = y; (*this)(2) = z; }
// This constructor allows you to construct Point3 from Eigen expressions
template<typename OtherDerived>
Point3(const Eigen::MatrixBase<OtherDerived> &other) : Vec3crd(other) {}
static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); }
// This method allows you to assign Eigen expressions to MyVectorType
template<typename OtherDerived>
Point3& operator=(const Eigen::MatrixBase<OtherDerived> &other)
{
this->Vec3crd::operator=(other);
return *this;
}
Point xy() const { return Point((*this)(0), (*this)(1)); }
};
std::ostream& operator<<(std::ostream &stm, const Pointf &pointf);
class Pointf : public Vec2d
{
public:
typedef coordf_t coord_type;
explicit Pointf() { (*this)(0) = (*this)(1) = 0.; }
explicit Pointf(coordf_t x, coordf_t y) { (*this)(0) = x; (*this)(1) = y; }
// This constructor allows you to construct Pointf from Eigen expressions
template<typename OtherDerived>
Pointf(const Eigen::MatrixBase<OtherDerived> &other) : Vec2d(other) {}
static Pointf new_unscale(coord_t x, coord_t y) { return Pointf(unscale(x), unscale(y)); }
static Pointf new_unscale(const Point &p) { return Pointf(unscale(p(0)), unscale(p(1))); }
// This method allows you to assign Eigen expressions to MyVectorType
template<typename OtherDerived>
Pointf& operator=(const Eigen::MatrixBase<OtherDerived> &other)
{
this->Vec2d::operator=(other);
return *this;
}
void rotate(double angle);
void rotate(double angle, const Pointf &center);
bool operator< (const Pointf& rhs) const { return (*this)(0) < rhs(0) || ((*this)(0) == rhs(0) && (*this)(1) < rhs(1)); }
};
class Pointf3 : public Vec3d
{
public:
typedef coordf_t coord_type;
explicit Pointf3() { (*this)(0) = (*this)(1) = (*this)(2) = 0.; }
explicit Pointf3(coordf_t x, coordf_t y, coordf_t z) { (*this)(0) = x; (*this)(1) = y; (*this)(2) = z; }
// This constructor allows you to construct Pointf from Eigen expressions
template<typename OtherDerived>
Pointf3(const Eigen::MatrixBase<OtherDerived> &other) : Vec3d(other) {}
static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) { return Pointf3(unscale(x), unscale(y), unscale(z)); }
static Pointf3 new_unscale(const Point3& p) { return Pointf3(unscale(p(0)), unscale(p(1)), unscale(p(2))); }
// This method allows you to assign Eigen expressions to MyVectorType
template<typename OtherDerived>
Pointf3& operator=(const Eigen::MatrixBase<OtherDerived> &other)
{
this->Vec3d::operator=(other);
return *this;
}
Pointf xy() const { return Pointf((*this)(0), (*this)(1)); }
};
} // namespace Slic3r } // namespace Slic3r

View file

@ -297,10 +297,10 @@ Point Polygon::point_projection(const Point &point) const
dmin = d; dmin = d;
proj = pt1; proj = pt1;
} }
Pointf v1(coordf_t(pt1(0) - pt0(0)), coordf_t(pt1(1) - pt0(1))); Vec2d v1(coordf_t(pt1(0) - pt0(0)), coordf_t(pt1(1) - pt0(1)));
coordf_t div = v1.squaredNorm(); coordf_t div = v1.squaredNorm();
if (div > 0.) { if (div > 0.) {
Pointf v2(coordf_t(point(0) - pt0(0)), coordf_t(point(1) - pt0(1))); Vec2d v2(coordf_t(point(0) - pt0(0)), coordf_t(point(1) - pt0(1)));
coordf_t t = v1.dot(v2) / div; coordf_t t = v1.dot(v2) / div;
if (t > 0. && t < 1.) { if (t > 0. && t < 1.) {
Point foot(coord_t(floor(coordf_t(pt0(0)) + t * v1(0) + 0.5)), coord_t(floor(coordf_t(pt0(1)) + t * v1(1) + 0.5))); Point foot(coord_t(floor(coordf_t(pt0(0)) + t * v1(0) + 0.5)), coord_t(floor(coordf_t(pt0(1)) + t * v1(1) + 0.5)));

View file

@ -24,11 +24,12 @@ public:
explicit Polygon(const Points &points): MultiPoint(points) {} explicit Polygon(const Points &points): MultiPoint(points) {}
Polygon(const Polygon &other) : MultiPoint(other.points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {}
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {} Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
static Polygon new_scale(std::vector<Pointf> points) { static Polygon new_scale(const std::vector<Vec2d> &points) {
Points int_points; Polygon pgn;
for (auto pt : points) pgn.points.reserve(points.size());
int_points.push_back(Point::new_scale(pt(0), pt(1))); for (const Vec2d &pt : points)
return Polygon(int_points); pgn.points.emplace_back(Point::new_scale(pt(0), pt(1)));
return pgn;
} }
Polygon& operator=(const Polygon &other) { points = other.points; return *this; } Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; } Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }

View file

@ -23,12 +23,11 @@ public:
explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); } explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
Polyline& operator=(const Polyline &other) { points = other.points; return *this; } Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; } Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
static Polyline new_scale(std::vector<Pointf> points) { static Polyline new_scale(const std::vector<Vec2d> &points) {
Polyline pl; Polyline pl;
Points int_points; pl.points.reserve(points.size());
for (auto pt : points) for (const Vec2d &pt : points)
int_points.push_back(Point::new_scale(pt(0), pt(1))); pl.points.emplace_back(Point::new_scale(pt(0), pt(1)));
pl.append(int_points);
return pl; return pl;
} }

View file

@ -540,7 +540,7 @@ bool Print::has_skirt() const
std::string Print::validate() const std::string Print::validate() const
{ {
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values)); BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Pointf3(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config.max_print_height)); BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(config.max_print_height)));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.min(2) = -1e10; print_volume.min(2) = -1e10;
unsigned int printable_count = 0; unsigned int printable_count = 0;
@ -728,7 +728,7 @@ BoundingBox Print::bounding_box() const
for (const PrintObject *object : this->objects) for (const PrintObject *object : this->objects)
for (Point copy : object->_shifted_copies) { for (Point copy : object->_shifted_copies) {
bb.merge(copy); bb.merge(copy);
copy += object->size.xy(); copy += to_2d(object->size);
bb.merge(copy); bb.merge(copy);
} }
return bb; return bb;
@ -971,7 +971,7 @@ void Print::_make_skirt()
this->skirt.append(eloop); this->skirt.append(eloop);
if (this->config.min_skirt_length.value > 0) { if (this->config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm. // The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx]; extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) { if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) {
// Not extruded enough yet with the current extruder. Add another loop. // Not extruded enough yet with the current extruder. Add another loop.
if (i == 1) if (i == 1)

View file

@ -118,7 +118,7 @@ public:
// so that next call to make_perimeters() performs a union() before computing loops // so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices; bool typed_slices;
Point3 size; // XYZ in scaled coordinates Vec3crd size; // XYZ in scaled coordinates
// scaled coordinates to add to copies (to compensate for the alignment // scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API // operated when creating the object but still preserving a coherent API
@ -138,13 +138,13 @@ public:
const ModelObject* model_object() const { return this->_model_object; } const ModelObject* model_object() const { return this->_model_object; }
const Points& copies() const { return this->_copies; } const Points& copies() const { return this->_copies; }
bool add_copy(const Pointf &point); bool add_copy(const Vec2d &point);
bool delete_last_copy(); bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); } bool delete_all_copies() { return this->set_copies(Points()); }
bool set_copies(const Points &points); bool set_copies(const Points &points);
bool reload_model_instances(); bool reload_model_instances();
// since the object is aligned to origin, bounding box coincides with size // since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), this->size.xy()); } BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
// adds region_id, too, if necessary // adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id) { void add_region_volume(unsigned int region_id, int volume_id) {

View file

@ -18,8 +18,50 @@ namespace Slic3r {
PrintConfigDef::PrintConfigDef() PrintConfigDef::PrintConfigDef()
{ {
t_optiondef_map &Options = this->options; this->init_common_params();
this->init_fff_params();
this->init_sla_params();
}
void PrintConfigDef::init_common_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
def = this->add("printer_technology", coEnum);
def->label = L("Printer technology");
def->tooltip = L("Printer technology");
def->cli = "printer-technology=s";
def->enum_keys_map = &ConfigOptionEnum<PrinterTechnology>::get_enum_values();
def->enum_values.push_back("FFF");
def->enum_values.push_back("SLA");
def->default_value = new ConfigOptionEnum<PrinterTechnology>(ptFFF);
def = this->add("bed_shape", coPoints);
def->label = L("Bed shape");
def->default_value = new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) };
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Layers and Perimeters");
def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
"Thinner layers give better accuracy but take more time to print.");
def->sidetext = L("mm");
def->cli = "layer-height=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(0.3);
def = this->add("max_print_height", coFloat);
def->label = L("Max print height");
def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
def->sidetext = L("mm");
def->cli = "max-print-height=f";
def->default_value = new ConfigOptionFloat(200.0);
}
void PrintConfigDef::init_fff_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def; ConfigOptionDef* def;
// Maximum extruder temperature, bumped to 1500 to support printing of glass. // Maximum extruder temperature, bumped to 1500 to support printing of glass.
@ -33,10 +75,6 @@ PrintConfigDef::PrintConfigDef()
def->cli = "avoid-crossing-perimeters!"; def->cli = "avoid-crossing-perimeters!";
def->default_value = new ConfigOptionBool(false); def->default_value = new ConfigOptionBool(false);
def = this->add("bed_shape", coPoints);
def->label = L("Bed shape");
def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) };
def = this->add("bed_temperature", coInts); def = this->add("bed_temperature", coInts);
def->label = L("Other layers"); def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers after the first one. " def->tooltip = L("Bed temperature for layers after the first one. "
@ -392,7 +430,7 @@ PrintConfigDef::PrintConfigDef()
"from the XY coordinate)."); "from the XY coordinate).");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->cli = "extruder-offset=s@"; def->cli = "extruder-offset=s@";
def->default_value = new ConfigOptionPoints { Pointf(0,0) }; def->default_value = new ConfigOptionPoints { Vec2d(0,0) };
def = this->add("extrusion_axis", coString); def = this->add("extrusion_axis", coString);
def->label = L("Extrusion axis"); def->label = L("Extrusion axis");
@ -906,16 +944,6 @@ PrintConfigDef::PrintConfigDef()
def->height = 50; def->height = 50;
def->default_value = new ConfigOptionString(""); def->default_value = new ConfigOptionString("");
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Layers and Perimeters");
def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
"Thinner layers give better accuracy but take more time to print.");
def->sidetext = L("mm");
def->cli = "layer-height=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(0.3);
def = this->add("remaining_times", coBool); def = this->add("remaining_times", coBool);
def->label = L("Supports remaining times"); def->label = L("Supports remaining times");
def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute" def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
@ -1036,13 +1064,6 @@ PrintConfigDef::PrintConfigDef()
def->min = 0; def->min = 0;
def->default_value = new ConfigOptionFloats { 0. }; def->default_value = new ConfigOptionFloats { 0. };
def = this->add("max_print_height", coFloat);
def->label = L("Max print height");
def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
def->sidetext = L("mm");
def->cli = "max-print-height=f";
def->default_value = new ConfigOptionFloat(200.0);
def = this->add("max_print_speed", coFloat); def = this->add("max_print_speed", coFloat);
def->label = L("Max print speed"); def->label = L("Max print speed");
def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed " def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed "
@ -1137,25 +1158,37 @@ PrintConfigDef::PrintConfigDef()
def->cli = "nozzle-diameter=f@"; def->cli = "nozzle-diameter=f@";
def->default_value = new ConfigOptionFloats { 0.5 }; def->default_value = new ConfigOptionFloats { 0.5 };
def = this->add("octoprint_apikey", coString); def = this->add("host_type", coEnum);
def->label = L("API Key"); def->label = L("Host Type");
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain " def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
"the API Key required for authentication."); "the kind of the host.");
def->cli = "octoprint-apikey=s"; def->cli = "host-type=s";
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
def->enum_values.push_back("octoprint");
def->enum_values.push_back("duet");
def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet");
def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
def = this->add("printhost_apikey", coString);
def->label = L("API Key / Password");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->cli = "printhost-apikey=s";
def->default_value = new ConfigOptionString(""); def->default_value = new ConfigOptionString("");
def = this->add("octoprint_cafile", coString); def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA file"; def->label = "HTTPS CA file";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used."; "If left blank, the default OS CA certificate repository is used.";
def->cli = "octoprint-cafile=s"; def->cli = "printhost-cafile=s";
def->default_value = new ConfigOptionString(""); def->default_value = new ConfigOptionString("");
def = this->add("octoprint_host", coString); def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL"); def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain " def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the OctoPrint instance."); "the hostname, IP address or URL of the printer host instance.");
def->cli = "octoprint-host=s"; def->cli = "print-host=s";
def->default_value = new ConfigOptionString(""); def->default_value = new ConfigOptionString("");
def = this->add("only_retract_when_crossing_perimeters", coBool); def = this->add("only_retract_when_crossing_perimeters", coBool);
@ -2121,6 +2154,103 @@ PrintConfigDef::PrintConfigDef()
def->default_value = new ConfigOptionFloat(35.); def->default_value = new ConfigOptionFloat(35.);
} }
void PrintConfigDef::init_sla_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// SLA Printer settings
def = this->add("display_width", coFloat);
def->label = L("Display width");
def->tooltip = L("Width of the display");
def->cli = "display-width=f";
def->min = 1;
def->default_value = new ConfigOptionFloat(150.);
def = this->add("display_height", coFloat);
def->label = L("Display height");
def->tooltip = L("Height of the display");
def->cli = "display-height=f";
def->min = 1;
def->default_value = new ConfigOptionFloat(100.);
def = this->add("display_pixels_x", coInt);
def->full_label = L("Number of pixels in");
def->label = ("X");
def->tooltip = L("Number of pixels in X");
def->cli = "display-pixels-x=i";
def->min = 100;
def->default_value = new ConfigOptionInt(2000);
def = this->add("display_pixels_y", coInt);
def->label = ("Y");
def->tooltip = L("Number of pixels in Y");
def->cli = "display-pixels-y=i";
def->min = 100;
def->default_value = new ConfigOptionInt(1000);
def = this->add("printer_correction", coFloats);
def->full_label = L("Printer scaling correction");
def->tooltip = L("Printer scaling correction");
def->min = 0;
def->default_value = new ConfigOptionFloats( { 1., 1., 1. } );
// SLA Material settings.
def = this->add("initial_layer_height", coFloat);
def->label = L("Initial layer height");
def->tooltip = L("Initial layer height");
def->sidetext = L("mm");
def->cli = "initial-layer-height=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(0.3);
def = this->add("exposure_time", coFloat);
def->label = L("Exposure time");
def->tooltip = L("Exposure time");
def->sidetext = L("s");
def->cli = "exposure-time=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(10);
def = this->add("initial_exposure_time", coFloat);
def->label = L("Initial exposure time");
def->tooltip = L("Initial exposure time");
def->sidetext = L("s");
def->cli = "initial-exposure-time=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(15);
def = this->add("material_correction_printing", coFloats);
def->full_label = L("Correction for expansion when printing");
def->tooltip = L("Correction for expansion when printing");
def->min = 0;
def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
def = this->add("material_correction_curing", coFloats);
def->full_label = L("Correction for expansion after curing");
def->tooltip = L("Correction for expansion after curing");
def->min = 0;
def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
def = this->add("material_notes", coString);
def->label = L("SLA print material notes");
def->tooltip = L("You can put your notes regarding the SLA print material here.");
def->cli = "material-notes=s";
def->multiline = true;
def->full_width = true;
def->height = 130;
def->default_value = new ConfigOptionString("");
def = this->add("default_sla_material_profile", coString);
def->label = L("Default SLA material profile");
def->tooltip = L("Default print profile associated with the current printer profile. "
"On selection of the current printer profile, this print profile will be activated.");
def->default_value = new ConfigOptionString();
def = this->add("sla_material_settings_id", coString);
def->default_value = new ConfigOptionString("");
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
{ {
// handle legacy options // handle legacy options
@ -2153,10 +2283,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
std::ostringstream oss; std::ostringstream oss;
oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1); oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1);
value = oss.str(); value = oss.str();
// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
// Commenting this out fixes github issue #869 for now.
// } else if (opt_key == "octoprint_host" && !value.empty()) {
// opt_key = "print_host";
} else if ((opt_key == "perimeter_acceleration" && value == "25") } else if ((opt_key == "perimeter_acceleration" && value == "25")
|| (opt_key == "infill_acceleration" && value == "50")) { || (opt_key == "infill_acceleration" && value == "50")) {
/* For historical reasons, the world's full of configs having these very low values; /* For historical reasons, the world's full of configs having these very low values;
@ -2167,6 +2293,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
} else if (opt_key == "support_material_pattern" && value == "pillars") { } else if (opt_key == "support_material_pattern" && value == "pillars") {
// Slic3r PE does not support the pillars. They never worked well. // Slic3r PE does not support the pillars. They never worked well.
value = "rectilinear"; value = "rectilinear";
} else if (opt_key == "octoprint_host") {
opt_key = "print_host";
} else if (opt_key == "octoprint_cafile") {
opt_key = "printhost_cafile";
} else if (opt_key == "octoprint_apikey") {
opt_key = "printhost_apikey";
} }
// Ignore the following obsolete configuration keys: // Ignore the following obsolete configuration keys:
@ -2176,9 +2308,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid", "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
"seal_position", "vibration_limit", "bed_size", "seal_position", "vibration_limit", "bed_size",
// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
// Commenting this out fixes github issue #869 for now.
// "octoprint_host",
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe" "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
}; };
@ -2188,7 +2317,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
} }
if (! print_config_def.has(opt_key)) { if (! print_config_def.has(opt_key)) {
//printf("Unknown option %s\n", opt_key.c_str());
opt_key = ""; opt_key = "";
return; return;
} }
@ -2446,4 +2574,8 @@ StaticPrintConfig::StaticCache<class Slic3r::PrintConfig> PrintConfig::s_c
StaticPrintConfig::StaticCache<class Slic3r::HostConfig> HostConfig::s_cache_HostConfig; StaticPrintConfig::StaticCache<class Slic3r::HostConfig> HostConfig::s_cache_HostConfig;
StaticPrintConfig::StaticCache<class Slic3r::FullPrintConfig> FullPrintConfig::s_cache_FullPrintConfig; StaticPrintConfig::StaticCache<class Slic3r::FullPrintConfig> FullPrintConfig::s_cache_FullPrintConfig;
StaticPrintConfig::StaticCache<class Slic3r::SLAMaterialConfig> SLAMaterialConfig::s_cache_SLAMaterialConfig;
StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig> SLAPrinterConfig::s_cache_SLAPrinterConfig;
StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig;
} }

View file

@ -22,11 +22,23 @@
namespace Slic3r { namespace Slic3r {
enum PrinterTechnology
{
// Fused Filament Fabrication
ptFFF,
// Stereolitography
ptSLA,
};
enum GCodeFlavor { enum GCodeFlavor {
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion, gcfSmoothie, gcfNoExtrusion,
}; };
enum PrintHostType {
htOctoPrint, htDuet,
};
enum InfillPattern { enum InfillPattern {
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
@ -44,7 +56,16 @@ enum FilamentType {
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
}; };
template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["FFF"] = ptFFF;
keys_map["SLA"] = ptSLA;
}
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
keys_map["reprap"] = gcfRepRap; keys_map["reprap"] = gcfRepRap;
@ -61,7 +82,16 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
return keys_map; return keys_map;
} }
template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["octoprint"] = htOctoPrint;
keys_map["duet"] = htDuet;
}
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
keys_map["rectilinear"] = ipRectilinear; keys_map["rectilinear"] = ipRectilinear;
@ -81,7 +111,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enu
return keys_map; return keys_map;
} }
template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
keys_map["rectilinear"] = smpRectilinear; keys_map["rectilinear"] = smpRectilinear;
@ -91,7 +121,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>
return keys_map; return keys_map;
} }
template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
keys_map["random"] = spRandom; keys_map["random"] = spRandom;
@ -102,7 +132,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum
return keys_map; return keys_map;
} }
template<> inline t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
keys_map["PLA"] = ftPLA; keys_map["PLA"] = ftPLA;
@ -126,6 +156,11 @@ public:
PrintConfigDef(); PrintConfigDef();
static void handle_legacy(t_config_option_key &opt_key, std::string &value); static void handle_legacy(t_config_option_key &opt_key, std::string &value);
private:
void init_common_params();
void init_fff_params();
void init_sla_params();
}; };
// The one and only global definition of SLic3r configuration options. // The one and only global definition of SLic3r configuration options.
@ -801,18 +836,20 @@ class HostConfig : public StaticPrintConfig
{ {
STATIC_PRINT_CONFIG_CACHE(HostConfig) STATIC_PRINT_CONFIG_CACHE(HostConfig)
public: public:
ConfigOptionString octoprint_host; ConfigOptionEnum<PrintHostType> host_type;
ConfigOptionString octoprint_apikey; ConfigOptionString print_host;
ConfigOptionString octoprint_cafile; ConfigOptionString printhost_apikey;
ConfigOptionString printhost_cafile;
ConfigOptionString serial_port; ConfigOptionString serial_port;
ConfigOptionInt serial_speed; ConfigOptionInt serial_speed;
protected: protected:
void initialize(StaticCacheBase &cache, const char *base_ptr) void initialize(StaticCacheBase &cache, const char *base_ptr)
{ {
OPT_PTR(octoprint_host); OPT_PTR(host_type);
OPT_PTR(octoprint_apikey); OPT_PTR(print_host);
OPT_PTR(octoprint_cafile); OPT_PTR(printhost_apikey);
OPT_PTR(printhost_cafile);
OPT_PTR(serial_port); OPT_PTR(serial_port);
OPT_PTR(serial_speed); OPT_PTR(serial_speed);
} }
@ -844,6 +881,73 @@ protected:
} }
}; };
class SLAMaterialConfig : public StaticPrintConfig
{
STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig)
public:
ConfigOptionFloat layer_height;
ConfigOptionFloat initial_layer_height;
ConfigOptionFloat exposure_time;
ConfigOptionFloat initial_exposure_time;
ConfigOptionFloats material_correction_printing;
ConfigOptionFloats material_correction_curing;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
OPT_PTR(layer_height);
OPT_PTR(initial_layer_height);
OPT_PTR(exposure_time);
OPT_PTR(initial_exposure_time);
OPT_PTR(material_correction_printing);
OPT_PTR(material_correction_curing);
}
};
class SLAPrinterConfig : public StaticPrintConfig
{
STATIC_PRINT_CONFIG_CACHE(SLAPrinterConfig)
public:
ConfigOptionEnum<PrinterTechnology> printer_technology;
ConfigOptionPoints bed_shape;
ConfigOptionFloat max_print_height;
ConfigOptionFloat display_width;
ConfigOptionFloat display_height;
ConfigOptionInt display_pixels_x;
ConfigOptionInt display_pixels_y;
ConfigOptionFloats printer_correction;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
OPT_PTR(printer_technology);
OPT_PTR(bed_shape);
OPT_PTR(max_print_height);
OPT_PTR(display_width);
OPT_PTR(display_height);
OPT_PTR(display_pixels_x);
OPT_PTR(display_pixels_y);
OPT_PTR(printer_correction);
}
};
class SLAFullPrintConfig : public SLAPrinterConfig, public SLAMaterialConfig
{
STATIC_PRINT_CONFIG_CACHE_DERIVED(SLAFullPrintConfig)
SLAFullPrintConfig() : SLAPrinterConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); }
public:
// Validate the SLAFullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
// std::string validate();
protected:
// Protected constructor to be called to initialize ConfigCache::m_default.
SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAMaterialConfig(0) {}
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
this->SLAPrinterConfig ::initialize(cache, base_ptr);
this->SLAMaterialConfig::initialize(cache, base_ptr);
}
};
#undef STATIC_PRINT_CONFIG_CACHE #undef STATIC_PRINT_CONFIG_CACHE
#undef STATIC_PRINT_CONFIG_CACHE_BASE #undef STATIC_PRINT_CONFIG_CACHE_BASE
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED #undef STATIC_PRINT_CONFIG_CACHE_DERIVED

View file

@ -38,6 +38,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
typed_slices(false), typed_slices(false),
_print(print), _print(print),
_model_object(model_object), _model_object(model_object),
size(Vec3crd::Zero()),
layer_height_profile_valid(false) layer_height_profile_valid(false)
{ {
// Compute the translation to be applied to our meshes so that we work with smaller coordinates // Compute the translation to be applied to our meshes so that we work with smaller coordinates
@ -50,8 +51,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
// (copies are expressed in G-code coordinates and this translation is not publicly exposed). // (copies are expressed in G-code coordinates and this translation is not publicly exposed).
this->_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1)); this->_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
// Scale the object size and store it // Scale the object size and store it
Pointf3 size = modobj_bbox.size(); this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
this->size = Point3::new_scale(size(0), size(1), size(2));
} }
this->reload_model_instances(); this->reload_model_instances();
@ -59,7 +59,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
this->layer_height_profile = model_object->layer_height_profile; this->layer_height_profile = model_object->layer_height_profile;
} }
bool PrintObject::add_copy(const Pointf &point) bool PrintObject::add_copy(const Vec2d &point)
{ {
Points points = this->_copies; Points points = this->_copies;
points.push_back(Point::new_scale(point(0), point(1))); points.push_back(Point::new_scale(point(0), point(1)));
@ -1121,7 +1121,7 @@ SlicingParameters PrintObject::slicing_parameters() const
{ {
return SlicingParameters::create_from_config( return SlicingParameters::create_from_config(
this->print()->config, this->config, this->print()->config, this->config,
unscale(this->size(2)), this->print()->object_extruders()); unscale<double>(this->size(2)), this->print()->object_extruders());
} }
bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const
@ -1335,7 +1335,7 @@ std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::
// consider the first one // consider the first one
this->model_object()->instances.front()->transform_mesh(&mesh, true); this->model_object()->instances.front()->transform_mesh(&mesh, true);
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh.translate(- float(unscale(this->_copies_shift(0))), - float(unscale(this->_copies_shift(1))), -float(this->model_object()->bounding_box().min(2))); mesh.translate(- unscale<float>(this->_copies_shift(0)), - unscale<float>(this->_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
// perform actual slicing // perform actual slicing
TriangleMeshSlicer mslicer(&mesh); TriangleMeshSlicer mslicer(&mesh);
mslicer.slice(z, &layers); mslicer.slice(z, &layers);

View file

@ -3,7 +3,7 @@
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#define COORD(x) ((float)unscale((x))*10) #define COORD(x) (unscale<float>((x))*10)
namespace Slic3r { namespace Slic3r {
@ -58,8 +58,8 @@ SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width) void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
{ {
Pointf dir(line.b(0)-line.a(0), line.b(1)-line.a(1)); Vec2d dir(line.b(0)-line.a(0), line.b(1)-line.a(1));
Pointf perp(-dir(1), dir(0)); Vec2d perp(-dir(1), dir(0));
coordf_t len = sqrt(perp(0)*perp(0) + perp(1)*perp(1)); coordf_t len = sqrt(perp(0)*perp(0) + perp(1)*perp(1));
coordf_t da = coordf_t(0.5)*line.a_width/len; coordf_t da = coordf_t(0.5)*line.a_width/len;
coordf_t db = coordf_t(0.5)*line.b_width/len; coordf_t db = coordf_t(0.5)*line.b_width/len;

View file

@ -561,15 +561,15 @@ int generate_layer_height_texture(
void *data, int rows, int cols, bool level_of_detail_2nd_level) void *data, int rows, int cols, bool level_of_detail_2nd_level)
{ {
// https://github.com/aschn/gnuplot-colorbrewer // https://github.com/aschn/gnuplot-colorbrewer
std::vector<Point3> palette_raw; std::vector<Vec3crd> palette_raw;
palette_raw.push_back(Point3(0x01A, 0x098, 0x050)); palette_raw.push_back(Vec3crd(0x01A, 0x098, 0x050));
palette_raw.push_back(Point3(0x066, 0x0BD, 0x063)); palette_raw.push_back(Vec3crd(0x066, 0x0BD, 0x063));
palette_raw.push_back(Point3(0x0A6, 0x0D9, 0x06A)); palette_raw.push_back(Vec3crd(0x0A6, 0x0D9, 0x06A));
palette_raw.push_back(Point3(0x0D9, 0x0F1, 0x0EB)); palette_raw.push_back(Vec3crd(0x0D9, 0x0F1, 0x0EB));
palette_raw.push_back(Point3(0x0FE, 0x0E6, 0x0EB)); palette_raw.push_back(Vec3crd(0x0FE, 0x0E6, 0x0EB));
palette_raw.push_back(Point3(0x0FD, 0x0AE, 0x061)); palette_raw.push_back(Vec3crd(0x0FD, 0x0AE, 0x061));
palette_raw.push_back(Point3(0x0F4, 0x06D, 0x043)); palette_raw.push_back(Vec3crd(0x0F4, 0x06D, 0x043));
palette_raw.push_back(Point3(0x0D7, 0x030, 0x027)); palette_raw.push_back(Vec3crd(0x0D7, 0x030, 0x027));
// Clear the main texture and the 2nd LOD level. // Clear the main texture and the 2nd LOD level.
// memset(data, 0, rows * cols * (level_of_detail_2nd_level ? 5 : 4)); // memset(data, 0, rows * cols * (level_of_detail_2nd_level ? 5 : 4));
@ -600,14 +600,14 @@ int generate_layer_height_texture(
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1); coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1]; const Vec3crd &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2]; const Vec3crd &color2 = palette_raw[idx2];
coordf_t z = cell_to_z * coordf_t(cell); coordf_t z = cell_to_z * coordf_t(cell);
assert(z >= lo && z <= hi); assert(z >= lo && z <= hi);
// Intensity profile to visualize the layers. // Intensity profile to visualize the layers.
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
// Color mapping from layer height to RGB. // Color mapping from layer height to RGB.
Pointf3 color( Vec3d color(
intensity * lerp(coordf_t(color1(0)), coordf_t(color2(0)), t), intensity * lerp(coordf_t(color1(0)), coordf_t(color2(0)), t),
intensity * lerp(coordf_t(color1(1)), coordf_t(color2(1)), t), intensity * lerp(coordf_t(color1(1)), coordf_t(color2(1)), t),
intensity * lerp(coordf_t(color1(2)), coordf_t(color2(2)), t)); intensity * lerp(coordf_t(color1(2)), coordf_t(color2(2)), t));
@ -636,10 +636,10 @@ int generate_layer_height_texture(
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1); coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1]; const Vec3crd &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2]; const Vec3crd &color2 = palette_raw[idx2];
// Color mapping from layer height to RGB. // Color mapping from layer height to RGB.
Pointf3 color( Vec3d color(
lerp(coordf_t(color1(0)), coordf_t(color2(0)), t), lerp(coordf_t(color1(0)), coordf_t(color2(0)), t),
lerp(coordf_t(color1(1)), coordf_t(color2(1)), t), lerp(coordf_t(color1(1)), coordf_t(color2(1)), t),
lerp(coordf_t(color1(2)), coordf_t(color2(2)), t)); lerp(coordf_t(color1(2)), coordf_t(color2(2)), t));

View file

@ -15,8 +15,8 @@ void SlicingAdaptive::clear()
std::pair<float, float> face_z_span(const stl_facet *f) std::pair<float, float> face_z_span(const stl_facet *f)
{ {
return std::pair<float, float>( return std::pair<float, float>(
std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z), std::min(std::min(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)),
std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z)); std::max(std::max(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)));
} }
void SlicingAdaptive::prepare() void SlicingAdaptive::prepare()
@ -40,7 +40,7 @@ void SlicingAdaptive::prepare()
// 3) Generate Z components of the facet normals. // 3) Generate Z components of the facet normals.
m_face_normal_z.assign(m_faces.size(), 0.f); m_face_normal_z.assign(m_faces.size(), 0.f);
for (size_t iface = 0; iface < m_faces.size(); ++ iface) for (size_t iface = 0; iface < m_faces.size(); ++ iface)
m_face_normal_z[iface] = m_faces[iface]->normal.z; m_face_normal_z[iface] = m_faces[iface]->normal(2);
} }
float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet) float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)

View file

@ -2057,8 +2057,8 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
const Point &p1 = *(it-1); const Point &p1 = *(it-1);
const Point &p2 = *it; const Point &p2 = *it;
// Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance. // Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance.
const Pointf v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1))); const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1)));
const Pointf v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1))); const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1)));
coordf_t a = v_seg.squaredNorm(); coordf_t a = v_seg.squaredNorm();
coordf_t b = 2. * v_seg.dot(v_cntr); coordf_t b = 2. * v_seg.dot(v_cntr);
coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance; coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance;

View file

@ -1,6 +1,9 @@
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "qhull/src/libqhullcpp/Qhull.h"
#include "qhull/src/libqhullcpp/QhullFacetList.h"
#include "qhull/src/libqhullcpp/QhullVertexSet.h"
#include <cmath> #include <cmath>
#include <deque> #include <deque>
#include <queue> #include <queue>
@ -15,6 +18,8 @@
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <Eigen/Dense>
#if 0 #if 0
#define DEBUG #define DEBUG
#define _DEBUG #define _DEBUG
@ -30,13 +35,7 @@
namespace Slic3r { namespace Slic3r {
TriangleMesh::TriangleMesh() TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets )
: repaired(false)
{
stl_initialize(&this->stl);
}
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& facets )
: repaired(false) : repaired(false)
{ {
stl_initialize(&this->stl); stl_initialize(&this->stl);
@ -51,51 +50,22 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& fa
for (int i = 0; i < stl.stats.number_of_facets; i++) { for (int i = 0; i < stl.stats.number_of_facets; i++) {
stl_facet facet; stl_facet facet;
facet.vertex[0] = points[facets[i](0)].cast<float>();
const Pointf3& ref_f1 = points[facets[i](0)]; facet.vertex[1] = points[facets[i](1)].cast<float>();
facet.vertex[0].x = ref_f1(0); facet.vertex[2] = points[facets[i](2)].cast<float>();
facet.vertex[0].y = ref_f1(1);
facet.vertex[0].z = ref_f1(2);
const Pointf3& ref_f2 = points[facets[i](1)];
facet.vertex[1].x = ref_f2(0);
facet.vertex[1].y = ref_f2(1);
facet.vertex[1].z = ref_f2(2);
const Pointf3& ref_f3 = points[facets[i](2)];
facet.vertex[2].x = ref_f3(0);
facet.vertex[2].y = ref_f3(1);
facet.vertex[2].z = ref_f3(2);
facet.extra[0] = 0; facet.extra[0] = 0;
facet.extra[1] = 0; facet.extra[1] = 0;
float normal[3]; stl_normal normal;
stl_calculate_normal(normal, &facet); stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal); stl_normalize_vector(normal);
facet.normal.x = normal[0]; facet.normal = normal;
facet.normal.y = normal[1];
facet.normal.z = normal[2];
stl.facet_start[i] = facet; stl.facet_start[i] = facet;
} }
stl_get_size(&stl); stl_get_size(&stl);
} }
TriangleMesh::TriangleMesh(const TriangleMesh &other) :
repaired(false)
{
stl_initialize(&this->stl);
*this = other;
}
TriangleMesh::TriangleMesh(TriangleMesh &&other) :
repaired(false)
{
stl_initialize(&this->stl);
this->swap(other);
}
TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
{ {
stl_close(&this->stl); stl_close(&this->stl);
@ -123,42 +93,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
return *this; return *this;
} }
TriangleMesh& TriangleMesh::operator=(TriangleMesh &&other) void TriangleMesh::repair()
{ {
this->swap(other);
return *this;
}
void
TriangleMesh::swap(TriangleMesh &other)
{
std::swap(this->stl, other.stl);
std::swap(this->repaired, other.repaired);
}
TriangleMesh::~TriangleMesh() {
stl_close(&this->stl);
}
void
TriangleMesh::ReadSTLFile(const char* input_file) {
stl_open(&stl, input_file);
}
void
TriangleMesh::write_ascii(const char* output_file)
{
stl_write_ascii(&this->stl, output_file, "");
}
void
TriangleMesh::write_binary(const char* output_file)
{
stl_write_binary(&this->stl, output_file, "");
}
void
TriangleMesh::repair() {
if (this->repaired) return; if (this->repaired) return;
// admesh fails when repairing empty meshes // admesh fails when repairing empty meshes
@ -255,13 +191,7 @@ void TriangleMesh::check_topology()
} }
} }
bool TriangleMesh::is_manifold() const void TriangleMesh::reset_repair_stats() {
{
return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets;
}
void
TriangleMesh::reset_repair_stats() {
this->stl.stats.degenerate_facets = 0; this->stl.stats.degenerate_facets = 0;
this->stl.stats.edges_fixed = 0; this->stl.stats.edges_fixed = 0;
this->stl.stats.facets_removed = 0; this->stl.stats.facets_removed = 0;
@ -271,8 +201,7 @@ TriangleMesh::reset_repair_stats() {
this->stl.stats.normals_fixed = 0; this->stl.stats.normals_fixed = 0;
} }
bool bool TriangleMesh::needed_repair() const
TriangleMesh::needed_repair() const
{ {
return this->stl.stats.degenerate_facets > 0 return this->stl.stats.degenerate_facets > 0
|| this->stl.stats.edges_fixed > 0 || this->stl.stats.edges_fixed > 0
@ -282,14 +211,8 @@ TriangleMesh::needed_repair() const
|| this->stl.stats.backwards_edges > 0; || this->stl.stats.backwards_edges > 0;
} }
size_t void TriangleMesh::WriteOBJFile(char* output_file)
TriangleMesh::facets_count() const
{ {
return this->stl.stats.number_of_facets;
}
void
TriangleMesh::WriteOBJFile(char* output_file) {
stl_generate_shared_vertices(&stl); stl_generate_shared_vertices(&stl);
stl_write_obj(&stl, output_file); stl_write_obj(&stl, output_file);
} }
@ -300,13 +223,9 @@ void TriangleMesh::scale(float factor)
stl_invalidate_shared_vertices(&this->stl); stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::scale(const Pointf3 &versor) void TriangleMesh::scale(const Vec3d &versor)
{ {
float fversor[3]; stl_scale_versor(&this->stl, versor.cast<float>());
fversor[0] = versor(0);
fversor[1] = versor(1);
fversor[2] = versor(2);
stl_scale_versor(&this->stl, fversor);
stl_invalidate_shared_vertices(&this->stl); stl_invalidate_shared_vertices(&this->stl);
} }
@ -336,21 +255,6 @@ void TriangleMesh::rotate(float angle, const Axis &axis)
stl_invalidate_shared_vertices(&this->stl); stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::rotate_x(float angle)
{
this->rotate(angle, X);
}
void TriangleMesh::rotate_y(float angle)
{
this->rotate(angle, Y);
}
void TriangleMesh::rotate_z(float angle)
{
this->rotate(angle, Z);
}
void TriangleMesh::mirror(const Axis &axis) void TriangleMesh::mirror(const Axis &axis)
{ {
if (axis == X) { if (axis == X) {
@ -363,21 +267,6 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl); stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::mirror_x()
{
this->mirror(X);
}
void TriangleMesh::mirror_y()
{
this->mirror(Y);
}
void TriangleMesh::mirror_z()
{
this->mirror(Z);
}
void TriangleMesh::transform(const float* matrix3x4) void TriangleMesh::transform(const float* matrix3x4)
{ {
if (matrix3x4 == nullptr) if (matrix3x4 == nullptr)
@ -390,10 +279,9 @@ void TriangleMesh::transform(const float* matrix3x4)
void TriangleMesh::align_to_origin() void TriangleMesh::align_to_origin()
{ {
this->translate( this->translate(
-(this->stl.stats.min.x), - this->stl.stats.min(0),
-(this->stl.stats.min.y), - this->stl.stats.min(1),
-(this->stl.stats.min.z) - this->stl.stats.min(2));
);
} }
void TriangleMesh::rotate(double angle, Point* center) void TriangleMesh::rotate(double angle, Point* center)
@ -476,14 +364,14 @@ size_t TriangleMesh::number_of_patches() const
return num_bodies; return num_bodies;
} }
TriangleMeshPtrs TriangleMeshPtrs TriangleMesh::split() const
TriangleMesh::split() const
{ {
TriangleMeshPtrs meshes; TriangleMeshPtrs meshes;
std::set<int> seen_facets; std::vector<unsigned char> facet_visited(this->stl.stats.number_of_facets, false);
// we need neighbors // we need neighbors
if (!this->repaired) CONFESS("split() requires repair()"); if (!this->repaired)
CONFESS("split() requires repair()");
// loop while we have remaining facets // loop while we have remaining facets
for (;;) { for (;;) {
@ -491,23 +379,24 @@ TriangleMesh::split() const
std::queue<int> facet_queue; std::queue<int> facet_queue;
std::deque<int> facets; std::deque<int> facets;
for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; facet_idx++) { for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; facet_idx++) {
if (seen_facets.find(facet_idx) == seen_facets.end()) { if (! facet_visited[facet_idx]) {
// if facet was not seen put it into queue and start searching // if facet was not seen put it into queue and start searching
facet_queue.push(facet_idx); facet_queue.push(facet_idx);
break; break;
} }
} }
if (facet_queue.empty()) break; if (facet_queue.empty())
break;
while (!facet_queue.empty()) { while (! facet_queue.empty()) {
int facet_idx = facet_queue.front(); int facet_idx = facet_queue.front();
facet_queue.pop(); facet_queue.pop();
if (seen_facets.find(facet_idx) != seen_facets.end()) continue; if (! facet_visited[facet_idx]) {
facets.emplace_back(facet_idx); facets.emplace_back(facet_idx);
for (int j = 0; j <= 2; j++) { for (int j = 0; j < 3; ++ j)
facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]); facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]);
facet_visited[facet_idx] = true;
} }
seen_facets.insert(facet_idx);
} }
TriangleMesh* mesh = new TriangleMesh; TriangleMesh* mesh = new TriangleMesh;
@ -518,19 +407,17 @@ TriangleMesh::split() const
stl_clear_error(&mesh->stl); stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl); stl_allocate(&mesh->stl);
int first = 1; bool first = true;
for (std::deque<int>::const_iterator facet = facets.begin(); facet != facets.end(); ++facet) { for (std::deque<int>::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) {
mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
first = 0;
} }
} }
return meshes; return meshes;
} }
void void TriangleMesh::merge(const TriangleMesh &mesh)
TriangleMesh::merge(const TriangleMesh &mesh)
{ {
// reset stats and metadata // reset stats and metadata
int number_of_facets = this->stl.stats.number_of_facets; int number_of_facets = this->stl.stats.number_of_facets;
@ -561,9 +448,9 @@ ExPolygons TriangleMesh::horizontal_projection() const
stl_facet* facet = &this->stl.facet_start[i]; stl_facet* facet = &this->stl.facet_start[i];
Polygon p; Polygon p;
p.points.resize(3); p.points.resize(3);
p.points[0] = Point::new_scale(facet->vertex[0].x, facet->vertex[0].y); p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1));
p.points[1] = Point::new_scale(facet->vertex[1].x, facet->vertex[1].y); p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1));
p.points[2] = Point::new_scale(facet->vertex[2].x, facet->vertex[2].y); p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1));
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
pp.emplace_back(p); pp.emplace_back(p);
} }
@ -578,28 +465,142 @@ Polygon TriangleMesh::convex_hull()
Points pp; Points pp;
pp.reserve(this->stl.stats.shared_vertices); pp.reserve(this->stl.stats.shared_vertices);
for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) { for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) {
stl_vertex* v = &this->stl.v_shared[i]; const stl_vertex &v = this->stl.v_shared[i];
pp.emplace_back(Point::new_scale(v->x, v->y)); pp.emplace_back(Point::new_scale(v(0), v(1)));
} }
return Slic3r::Geometry::convex_hull(pp); return Slic3r::Geometry::convex_hull(pp);
} }
BoundingBoxf3 BoundingBoxf3 TriangleMesh::bounding_box() const
TriangleMesh::bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
bb.defined = true; bb.defined = true;
bb.min(0) = this->stl.stats.min.x; bb.min = this->stl.stats.min.cast<double>();
bb.min(1) = this->stl.stats.min.y; bb.max = this->stl.stats.max.cast<double>();
bb.min(2) = this->stl.stats.min.z;
bb.max(0) = this->stl.stats.max.x;
bb.max(1) = this->stl.stats.max.y;
bb.max(2) = this->stl.stats.max.z;
return bb; return bb;
} }
void BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
TriangleMesh::require_shared_vertices() {
bool has_shared = (stl.v_shared != nullptr);
if (!has_shared)
stl_generate_shared_vertices(&stl);
unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
if (vertices_count == 0)
return BoundingBoxf3();
Eigen::MatrixXd src_vertices(3, vertices_count);
if (stl.stats.shared_vertices > 0)
{
stl_vertex* vertex_ptr = stl.v_shared;
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
src_vertices(0, i) = (double)(*vertex_ptr)(0);
src_vertices(1, i) = (double)(*vertex_ptr)(1);
src_vertices(2, i) = (double)(*vertex_ptr)(2);
vertex_ptr += 1;
}
}
else
{
stl_facet* facet_ptr = stl.facet_start;
unsigned int v_id = 0;
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
{
for (int i = 0; i < 3; ++i)
{
src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0);
src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1);
src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2);
}
facet_ptr += 1;
++v_id;
}
}
if (!has_shared && (stl.stats.shared_vertices > 0))
stl_invalidate_shared_vertices(&stl);
Eigen::MatrixXd dst_vertices(3, vertices_count);
dst_vertices = t * src_vertices.colwise().homogeneous();
Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
Vec3d v_max = v_min;
for (int i = 1; i < vertices_count; ++i)
{
for (int j = 0; j < 3; ++j)
{
v_min(j) = std::min(v_min(j), dst_vertices(j, i));
v_max(j) = std::max(v_max(j), dst_vertices(j, i));
}
}
return BoundingBoxf3(v_min, v_max);
}
TriangleMesh TriangleMesh::convex_hull_3d() const
{
// Helper struct for qhull:
struct PointForQHull{
PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
realT x, y, z;
};
std::vector<PointForQHull> src_vertices;
// We will now fill the vector with input points for computation:
stl_facet* facet_ptr = stl.facet_start;
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
{
for (int i = 0; i < 3; ++i)
{
const stl_vertex& v = facet_ptr->vertex[i];
src_vertices.emplace_back(v(0), v(1), v(2));
}
facet_ptr += 1;
}
// The qhull call:
orgQhull::Qhull qhull;
qhull.disableOutputStream(); // we want qhull to be quiet
try
{
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
}
catch (...)
{
std::cout << "Unable to create convex hull" << std::endl;
return TriangleMesh();
}
// Let's collect results:
Pointf3s dst_vertices;
std::vector<Vec3crd> facets;
auto facet_list = qhull.facetList().toStdVector();
for (const orgQhull::QhullFacet& facet : facet_list)
{ // iterate through facets
orgQhull::QhullVertexSet vertices = facet.vertices();
for (int i = 0; i < 3; ++i)
{ // iterate through facet's vertices
orgQhull::QhullPoint p = vertices[i].point();
const float* coords = p.coordinates();
dst_vertices.emplace_back(coords[0], coords[1], coords[2]);
}
unsigned int size = (unsigned int)dst_vertices.size();
facets.emplace_back(size - 3, size - 2, size - 1);
}
TriangleMesh output_mesh(dst_vertices, facets);
output_mesh.repair();
return output_mesh;
}
void TriangleMesh::require_shared_vertices()
{ {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
if (!this->repaired) if (!this->repaired)
@ -619,11 +620,8 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);
// Scale the copied vertices. // Scale the copied vertices.
for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) { for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i)
this->v_scaled_shared[i].x /= float(SCALING_FACTOR); this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR);
this->v_scaled_shared[i].y /= float(SCALING_FACTOR);
this->v_scaled_shared[i].z /= float(SCALING_FACTOR);
}
// Create a mapping from triangle edge into face. // Create a mapping from triangle edge into face.
struct EdgeToFace { struct EdgeToFace {
@ -697,8 +695,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
} }
} }
void void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
{ {
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@ -779,14 +776,14 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
// find facet extents // find facet extents
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
printf("z: min = %.2f, max = %.2f\n", min_z, max_z); printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif #endif
@ -806,18 +803,18 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
if (il.edge_type == feHorizontal) { if (il.edge_type == feHorizontal) {
// Insert all three edges of the face. // Insert all three edges of the face.
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal.z < 0; const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < 3; ++ j) {
int a_id = vertices[j % 3]; int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3]; int b_id = vertices[(j+1) % 3];
if (reverse) if (reverse)
std::swap(a_id, b_id); std::swap(a_id, b_id);
const stl_vertex *a = &this->v_scaled_shared[a_id]; const stl_vertex &a = this->v_scaled_shared[a_id];
const stl_vertex *b = &this->v_scaled_shared[b_id]; const stl_vertex &b = this->v_scaled_shared[b_id];
il.a(0) = a->x; il.a(0) = a(0);
il.a(1) = a->y; il.a(1) = a(1);
il.b(0) = b->x; il.b(0) = b(0);
il.b(1) = b->y; il.b(1) = b(1);
il.a_id = a_id; il.a_id = a_id;
il.b_id = b_id; il.b_id = b_id;
(*lines)[layer_idx].emplace_back(il); (*lines)[layer_idx].emplace_back(il);
@ -863,66 +860,63 @@ bool TriangleMeshSlicer::slice_facet(
// Reorder vertices so that the first one is the one with lowest Z. // Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order // This is needed to get all intersection lines in a consistent order
// (external on the right of the line) // (external on the right of the line)
int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0); int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
for (int j = i; j - i < 3; ++ j) { // loop through facet edges for (int j = i; j - i < 3; ++ j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)]; int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3]; int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3]; int b_id = vertices[(j+1) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id]; const stl_vertex &a = this->v_scaled_shared[a_id];
const stl_vertex *b = &this->v_scaled_shared[b_id]; const stl_vertex &b = this->v_scaled_shared[b_id];
// Is edge or face aligned with the cutting plane? // Is edge or face aligned with the cutting plane?
if (a->z == slice_z && b->z == slice_z) { if (a(2) == slice_z && b(2) == slice_z) {
// Edge is horizontal and belongs to the current layer. // Edge is horizontal and belongs to the current layer.
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]]; const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]]; const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]]; const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
bool swap = false;
if (min_z == max_z) { if (min_z == max_z) {
// All three vertices are aligned with slice_z. // All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal; line_out->edge_type = feHorizontal;
if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) { if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order. // If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b); swap = true;
std::swap(a_id, b_id);
} }
} else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) { } else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane. // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
line_out->edge_type = feTop; line_out->edge_type = feTop;
std::swap(a, b); swap = true;
std::swap(a_id, b_id);
} else { } else {
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
line_out->edge_type = feBottom; line_out->edge_type = feBottom;
} }
line_out->a(0) = a->x; line_out->a = to_2d(swap ? b : a).cast<coord_t>();
line_out->a(1) = a->y; line_out->b = to_2d(swap ? a : b).cast<coord_t>();
line_out->b(0) = b->x; line_out->a_id = swap ? b_id : a_id;
line_out->b(1) = b->y; line_out->b_id = swap ? a_id : b_id;
line_out->a_id = a_id;
line_out->b_id = b_id;
return true; return true;
} }
if (a->z == slice_z) { if (a(2) == slice_z) {
// Only point a alings with the cutting plane. // Only point a alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points; points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++]; IntersectionPoint &point = points[num_points ++];
point(0) = a->x; point(0) = a(0);
point(1) = a->y; point(1) = a(1);
point.point_id = a_id; point.point_id = a_id;
} else if (b->z == slice_z) { } else if (b(2) == slice_z) {
// Only point b alings with the cutting plane. // Only point b alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points; points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++]; IntersectionPoint &point = points[num_points ++];
point(0) = b->x; point(0) = b(0);
point(1) = b->y; point(1) = b(1);
point.point_id = b_id; point.point_id = b_id;
} else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { } else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point. // A general case. The face edge intersects the cutting plane. Calculate the intersection point.
IntersectionPoint &point = points[num_points ++]; IntersectionPoint &point = points[num_points ++];
point(0) = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z); point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
point(1) = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z); point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
point.edge_id = edge_id; point.edge_id = edge_id;
} }
} }
@ -1389,8 +1383,8 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents // find facet extents
float min_z = std::min(facet->vertex[0].z, std::min(facet->vertex[1].z, facet->vertex[2].z)); float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2)));
float max_z = std::max(facet->vertex[0].z, std::max(facet->vertex[1].z, facet->vertex[2].z)); float max_z = std::max(facet->vertex[0](2), std::max(facet->vertex[1](2), facet->vertex[2](2)));
// intersect facet with cutting plane // intersect facet with cutting plane
IntersectionLine line; IntersectionLine line;
@ -1417,47 +1411,47 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// look for the vertex on whose side of the slicing plane there are no other vertices // look for the vertex on whose side of the slicing plane there are no other vertices
int isolated_vertex; int isolated_vertex;
if ( (facet->vertex[0].z > z) == (facet->vertex[1].z > z) ) { if ( (facet->vertex[0](2) > z) == (facet->vertex[1](2) > z) ) {
isolated_vertex = 2; isolated_vertex = 2;
} else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > z) ) { } else if ( (facet->vertex[1](2) > z) == (facet->vertex[2](2) > z) ) {
isolated_vertex = 0; isolated_vertex = 0;
} else { } else {
isolated_vertex = 1; isolated_vertex = 1;
} }
// get vertices starting from the isolated one // get vertices starting from the isolated one
stl_vertex* v0 = &facet->vertex[isolated_vertex]; const stl_vertex &v0 = facet->vertex[isolated_vertex];
stl_vertex* v1 = &facet->vertex[(isolated_vertex+1) % 3]; const stl_vertex &v1 = facet->vertex[(isolated_vertex+1) % 3];
stl_vertex* v2 = &facet->vertex[(isolated_vertex+2) % 3]; const stl_vertex &v2 = facet->vertex[(isolated_vertex+2) % 3];
// intersect v0-v1 and v2-v0 with cutting plane and make new vertices // intersect v0-v1 and v2-v0 with cutting plane and make new vertices
stl_vertex v0v1, v2v0; stl_vertex v0v1, v2v0;
v0v1.x = v1->x + (v0->x - v1->x) * (z - v1->z) / (v0->z - v1->z); v0v1(0) = v1(0) + (v0(0) - v1(0)) * (z - v1(2)) / (v0(2) - v1(2));
v0v1.y = v1->y + (v0->y - v1->y) * (z - v1->z) / (v0->z - v1->z); v0v1(1) = v1(1) + (v0(1) - v1(1)) * (z - v1(2)) / (v0(2) - v1(2));
v0v1.z = z; v0v1(2) = z;
v2v0.x = v2->x + (v0->x - v2->x) * (z - v2->z) / (v0->z - v2->z); v2v0(0) = v2(0) + (v0(0) - v2(0)) * (z - v2(2)) / (v0(2) - v2(2));
v2v0.y = v2->y + (v0->y - v2->y) * (z - v2->z) / (v0->z - v2->z); v2v0(1) = v2(1) + (v0(1) - v2(1)) * (z - v2(2)) / (v0(2) - v2(2));
v2v0.z = z; v2v0(2) = z;
// build the triangular facet // build the triangular facet
stl_facet triangle; stl_facet triangle;
triangle.normal = facet->normal; triangle.normal = facet->normal;
triangle.vertex[0] = *v0; triangle.vertex[0] = v0;
triangle.vertex[1] = v0v1; triangle.vertex[1] = v0v1;
triangle.vertex[2] = v2v0; triangle.vertex[2] = v2v0;
// build the facets forming a quadrilateral on the other side // build the facets forming a quadrilateral on the other side
stl_facet quadrilateral[2]; stl_facet quadrilateral[2];
quadrilateral[0].normal = facet->normal; quadrilateral[0].normal = facet->normal;
quadrilateral[0].vertex[0] = *v1; quadrilateral[0].vertex[0] = v1;
quadrilateral[0].vertex[1] = *v2; quadrilateral[0].vertex[1] = v2;
quadrilateral[0].vertex[2] = v0v1; quadrilateral[0].vertex[2] = v0v1;
quadrilateral[1].normal = facet->normal; quadrilateral[1].normal = facet->normal;
quadrilateral[1].vertex[0] = *v2; quadrilateral[1].vertex[0] = v2;
quadrilateral[1].vertex[1] = v2v0; quadrilateral[1].vertex[1] = v2v0;
quadrilateral[1].vertex[2] = v0v1; quadrilateral[1].vertex[2] = v0v1;
if (v0->z > z) { if (v0(2) > z) {
if (upper != NULL) stl_add_facet(&upper->stl, &triangle); if (upper != NULL) stl_add_facet(&upper->stl, &triangle);
if (lower != NULL) { if (lower != NULL) {
stl_add_facet(&lower->stl, &quadrilateral[0]); stl_add_facet(&lower->stl, &quadrilateral[0]);
@ -1489,13 +1483,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
Polygon p = *polygon; Polygon p = *polygon;
p.reverse(); p.reverse();
stl_facet facet; stl_facet facet;
facet.normal.x = 0; facet.normal = stl_normal(0, 0, -1.f);
facet.normal.y = 0;
facet.normal.z = -1;
for (size_t i = 0; i <= 2; ++i) { for (size_t i = 0; i <= 2; ++i) {
facet.vertex[i].x = unscale(p.points[i](0)); facet.vertex[i](0) = unscale<float>(p.points[i](0));
facet.vertex[i].y = unscale(p.points[i](1)); facet.vertex[i](1) = unscale<float>(p.points[i](1));
facet.vertex[i].z = z; facet.vertex[i](2) = z;
} }
stl_add_facet(&upper->stl, &facet); stl_add_facet(&upper->stl, &facet);
} }
@ -1515,13 +1507,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// convert triangles to facets and append them to mesh // convert triangles to facets and append them to mesh
for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) {
stl_facet facet; stl_facet facet;
facet.normal.x = 0; facet.normal = stl_normal(0, 0, 1.f);
facet.normal.y = 0;
facet.normal.z = 1;
for (size_t i = 0; i <= 2; ++i) { for (size_t i = 0; i <= 2; ++i) {
facet.vertex[i].x = unscale(polygon->points[i](0)); facet.vertex[i](0) = unscale<float>(polygon->points[i](0));
facet.vertex[i].y = unscale(polygon->points[i](1)); facet.vertex[i](1) = unscale<float>(polygon->points[i](1));
facet.vertex[i].z = z; facet.vertex[i](2) = z;
} }
stl_add_facet(&lower->stl, &facet); stl_add_facet(&lower->stl, &facet);
} }
@ -1534,19 +1524,19 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
TriangleMesh make_cube(double x, double y, double z) { TriangleMesh make_cube(double x, double y, double z) {
Pointf3 pv[8] = { Vec3d pv[8] = {
Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), Vec3d(x, y, 0), Vec3d(x, 0, 0), Vec3d(0, 0, 0),
Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z), Vec3d(0, y, 0), Vec3d(x, y, z), Vec3d(0, y, z),
Pointf3(0, 0, z), Pointf3(x, 0, z) Vec3d(0, 0, z), Vec3d(x, 0, z)
}; };
Point3 fv[12] = { Vec3crd fv[12] = {
Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6), Vec3crd(0, 1, 2), Vec3crd(0, 2, 3), Vec3crd(4, 5, 6),
Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1), Vec3crd(4, 6, 7), Vec3crd(0, 4, 7), Vec3crd(0, 7, 1),
Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5), Vec3crd(1, 7, 6), Vec3crd(1, 6, 2), Vec3crd(2, 6, 5),
Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5) Vec3crd(2, 5, 3), Vec3crd(4, 0, 3), Vec3crd(4, 3, 5)
}; };
std::vector<Point3> facets(&fv[0], &fv[0]+12); std::vector<Vec3crd> facets(&fv[0], &fv[0]+12);
Pointf3s vertices(&pv[0], &pv[0]+8); Pointf3s vertices(&pv[0], &pv[0]+8);
TriangleMesh mesh(vertices ,facets); TriangleMesh mesh(vertices ,facets);
@ -1558,11 +1548,11 @@ TriangleMesh make_cube(double x, double y, double z) {
// Default is 360 sides, angle fa is in radians. // Default is 360 sides, angle fa is in radians.
TriangleMesh make_cylinder(double r, double h, double fa) { TriangleMesh make_cylinder(double r, double h, double fa) {
Pointf3s vertices; Pointf3s vertices;
std::vector<Point3> facets; std::vector<Vec3crd> facets;
// 2 special vertices, top and bottom center, rest are relative to this // 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Pointf3(0.0, 0.0, 0.0)); vertices.emplace_back(Vec3d(0.0, 0.0, 0.0));
vertices.emplace_back(Pointf3(0.0, 0.0, h)); vertices.emplace_back(Vec3d(0.0, 0.0, h));
// adjust via rounding to get an even multiple for any provided angle. // adjust via rounding to get an even multiple for any provided angle.
double angle = (2*PI / floor(2*PI / fa)); double angle = (2*PI / floor(2*PI / fa));
@ -1572,24 +1562,23 @@ TriangleMesh make_cylinder(double r, double h, double fa) {
// top and bottom. // top and bottom.
// Special case: Last line shares 2 vertices with the first line. // Special case: Last line shares 2 vertices with the first line.
unsigned id = vertices.size() - 1; unsigned id = vertices.size() - 1;
vertices.emplace_back(Pointf3(sin(0) * r , cos(0) * r, 0)); vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, 0));
vertices.emplace_back(Pointf3(sin(0) * r , cos(0) * r, h)); vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, h));
for (double i = 0; i < 2*PI; i+=angle) { for (double i = 0; i < 2*PI; i+=angle) {
Pointf p(0, r); Vec2d p = Eigen::Rotation2Dd(i) * Eigen::Vector2d(0, r);
p.rotate(i); vertices.emplace_back(Vec3d(p(0), p(1), 0.));
vertices.emplace_back(Pointf3(p(0), p(1), 0.)); vertices.emplace_back(Vec3d(p(0), p(1), h));
vertices.emplace_back(Pointf3(p(0), p(1), h));
id = vertices.size() - 1; id = vertices.size() - 1;
facets.emplace_back(Point3( 0, id - 1, id - 3)); // top facets.emplace_back(Vec3crd( 0, id - 1, id - 3)); // top
facets.emplace_back(Point3(id, 1, id - 2)); // bottom facets.emplace_back(Vec3crd(id, 1, id - 2)); // bottom
facets.emplace_back(Point3(id, id - 2, id - 3)); // upper-right of side facets.emplace_back(Vec3crd(id, id - 2, id - 3)); // upper-right of side
facets.emplace_back(Point3(id, id - 3, id - 1)); // bottom-left of side facets.emplace_back(Vec3crd(id, id - 3, id - 1)); // bottom-left of side
} }
// Connect the last set of vertices with the first. // Connect the last set of vertices with the first.
facets.emplace_back(Point3( 2, 0, id - 1)); facets.emplace_back(Vec3crd( 2, 0, id - 1));
facets.emplace_back(Point3( 1, 3, id)); facets.emplace_back(Vec3crd( 1, 3, id));
facets.emplace_back(Point3(id, 3, 2)); facets.emplace_back(Vec3crd(id, 3, 2));
facets.emplace_back(Point3(id, 2, id - 1)); facets.emplace_back(Vec3crd(id, 2, id - 1));
TriangleMesh mesh(vertices, facets); TriangleMesh mesh(vertices, facets);
return mesh; return mesh;
@ -1600,7 +1589,7 @@ TriangleMesh make_cylinder(double r, double h, double fa) {
// Default angle is 1 degree. // Default angle is 1 degree.
TriangleMesh make_sphere(double rho, double fa) { TriangleMesh make_sphere(double rho, double fa) {
Pointf3s vertices; Pointf3s vertices;
std::vector<Point3> facets; std::vector<Vec3crd> facets;
// Algorithm: // Algorithm:
// Add points one-by-one to the sphere grid and form facets using relative coordinates. // Add points one-by-one to the sphere grid and form facets using relative coordinates.
@ -1619,17 +1608,16 @@ TriangleMesh make_sphere(double rho, double fa) {
// special case: first ring connects to 0,0,0 // special case: first ring connects to 0,0,0
// insert and form facets. // insert and form facets.
vertices.emplace_back(Pointf3(0.0, 0.0, -rho)); vertices.emplace_back(Vec3d(0.0, 0.0, -rho));
size_t id = vertices.size(); size_t id = vertices.size();
for (size_t i = 0; i < ring.size(); i++) { for (size_t i = 0; i < ring.size(); i++) {
// Fixed scaling // Fixed scaling
const double z = -rho + increment*rho*2.0; const double z = -rho + increment*rho*2.0;
// radius of the circle for this step. // radius of the circle for this step.
const double r = sqrt(abs(rho*rho - z*z)); const double r = sqrt(abs(rho*rho - z*z));
Pointf b(0, r); Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
b.rotate(ring[i]); vertices.emplace_back(Vec3d(b(0), b(1), z));
vertices.emplace_back(Pointf3(b(0), b(1), z)); facets.emplace_back((i == 0) ? Vec3crd(1, 0, ring.size()) : Vec3crd(id, 0, id - 1));
facets.emplace_back((i == 0) ? Point3(1, 0, ring.size()) : Point3(id, 0, id - 1));
++ id; ++ id;
} }
@ -1639,16 +1627,15 @@ TriangleMesh make_sphere(double rho, double fa) {
const double r = sqrt(abs(rho*rho - z*z)); const double r = sqrt(abs(rho*rho - z*z));
for (size_t i = 0; i < ring.size(); i++) { for (size_t i = 0; i < ring.size(); i++) {
Pointf b(0, r); Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
b.rotate(ring[i]); vertices.emplace_back(Vec3d(b(0), b(1), z));
vertices.emplace_back(Pointf3(b(0), b(1), z));
if (i == 0) { if (i == 0) {
// wrap around // wrap around
facets.emplace_back(Point3(id + ring.size() - 1 , id, id - 1)); facets.emplace_back(Vec3crd(id + ring.size() - 1 , id, id - 1));
facets.emplace_back(Point3(id, id - ring.size(), id - 1)); facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else { } else {
facets.emplace_back(Point3(id , id - ring.size(), (id - 1) - ring.size())); facets.emplace_back(Vec3crd(id , id - ring.size(), (id - 1) - ring.size()));
facets.emplace_back(Point3(id, id - 1 - ring.size() , id - 1)); facets.emplace_back(Vec3crd(id, id - 1 - ring.size() , id - 1));
} }
id++; id++;
} }
@ -1657,13 +1644,13 @@ TriangleMesh make_sphere(double rho, double fa) {
// special case: last ring connects to 0,0,rho*2.0 // special case: last ring connects to 0,0,rho*2.0
// only form facets. // only form facets.
vertices.emplace_back(Pointf3(0.0, 0.0, rho)); vertices.emplace_back(Vec3d(0.0, 0.0, rho));
for (size_t i = 0; i < ring.size(); i++) { for (size_t i = 0; i < ring.size(); i++) {
if (i == 0) { if (i == 0) {
// third vertex is on the other side of the ring. // third vertex is on the other side of the ring.
facets.emplace_back(Point3(id, id - ring.size(), id - 1)); facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else { } else {
facets.emplace_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1))); facets.emplace_back(Vec3crd(id, id - ring.size() + i, id - ring.size() + (i - 1)));
} }
} }
id++; id++;

View file

@ -20,33 +20,33 @@ typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
class TriangleMesh class TriangleMesh
{ {
public: public:
TriangleMesh(); TriangleMesh() : repaired(false) { stl_initialize(&this->stl); }
TriangleMesh(const Pointf3s &points, const std::vector<Point3> &facets); TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets);
TriangleMesh(const TriangleMesh &other); TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; }
TriangleMesh(TriangleMesh &&other); TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); }
~TriangleMesh() { stl_close(&this->stl); }
TriangleMesh& operator=(const TriangleMesh &other); TriangleMesh& operator=(const TriangleMesh &other);
TriangleMesh& operator=(TriangleMesh &&other); TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; }
void swap(TriangleMesh &other); void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
~TriangleMesh(); void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
void ReadSTLFile(const char* input_file); void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
void write_ascii(const char* output_file); void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); }
void write_binary(const char* output_file);
void repair(); void repair();
float volume(); float volume();
void check_topology(); void check_topology();
bool is_manifold() const; bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; }
void WriteOBJFile(char* output_file); void WriteOBJFile(char* output_file);
void scale(float factor); void scale(float factor);
void scale(const Pointf3 &versor); void scale(const Vec3d &versor);
void translate(float x, float y, float z); void translate(float x, float y, float z);
void rotate(float angle, const Axis &axis); void rotate(float angle, const Axis &axis);
void rotate_x(float angle); void rotate_x(float angle) { this->rotate(angle, X); }
void rotate_y(float angle); void rotate_y(float angle) { this->rotate(angle, Y); }
void rotate_z(float angle); void rotate_z(float angle) { this->rotate(angle, Z); }
void mirror(const Axis &axis); void mirror(const Axis &axis);
void mirror_x(); void mirror_x() { this->mirror(X); }
void mirror_y(); void mirror_y() { this->mirror(Y); }
void mirror_z(); void mirror_z() { this->mirror(Z); }
void transform(const float* matrix3x4); void transform(const float* matrix3x4);
void align_to_origin(); void align_to_origin();
void rotate(double angle, Point* center); void rotate(double angle, Point* center);
@ -55,9 +55,13 @@ public:
ExPolygons horizontal_projection() const; ExPolygons horizontal_projection() const;
Polygon convex_hull(); Polygon convex_hull();
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d& t) const;
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
void reset_repair_stats(); void reset_repair_stats();
bool needed_repair() const; bool needed_repair() const;
size_t facets_count() const; size_t facets_count() const { return this->stl.stats.number_of_facets; }
// Returns true, if there are two and more connected patches in the mesh. // Returns true, if there are two and more connected patches in the mesh.
// Returns false, if one or zero connected patch is in the mesh. // Returns false, if one or zero connected patch is in the mesh.
@ -66,7 +70,7 @@ public:
// Count disconnected triangle patches. // Count disconnected triangle patches.
size_t number_of_patches() const; size_t number_of_patches() const;
stl_file stl; mutable stl_file stl;
bool repaired; bool repaired;
private: private:

View file

@ -45,7 +45,6 @@ typedef double coordf_t;
//FIXME Better to use an inline function with an explicit return type. //FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR) #define scale_(val) ((val) / SCALING_FACTOR)
#define unscale(val) ((val) * SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON) #define SCALED_EPSILON scale_(EPSILON)
/* Implementation of CONFESS("foo"): */ /* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER #ifdef _MSC_VER
@ -102,6 +101,9 @@ inline std::string debug_out_path(const char *name, ...)
namespace Slic3r { namespace Slic3r {
template<typename T, typename Q>
inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); }
enum Axis { X=0, Y, Z, E, F, NUM_AXES }; enum Axis { X=0, Y, Z, E, F, NUM_AXES };
template <class T> template <class T>

View file

@ -41,9 +41,8 @@ REGISTER_CLASS(BoundingBoxf, "Geometry::BoundingBoxf");
REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3"); REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3");
REGISTER_CLASS(BridgeDetector, "BridgeDetector"); REGISTER_CLASS(BridgeDetector, "BridgeDetector");
REGISTER_CLASS(Point, "Point"); REGISTER_CLASS(Point, "Point");
REGISTER_CLASS(Point3, "Point3"); __REGISTER_CLASS(Vec2d, "Pointf");
REGISTER_CLASS(Pointf, "Pointf"); __REGISTER_CLASS(Vec3d, "Pointf3");
REGISTER_CLASS(Pointf3, "Pointf3");
REGISTER_CLASS(DynamicPrintConfig, "Config"); REGISTER_CLASS(DynamicPrintConfig, "Config");
REGISTER_CLASS(StaticPrintConfig, "Config::Static"); REGISTER_CLASS(StaticPrintConfig, "Config::Static");
REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject"); REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject");
@ -64,9 +63,9 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle"); REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(TabIface, "GUI::Tab"); REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(PresetUpdater, "PresetUpdater"); REGISTER_CLASS(PresetUpdater, "PresetUpdater");
REGISTER_CLASS(OctoPrint, "OctoPrint");
REGISTER_CLASS(AppController, "AppController"); REGISTER_CLASS(AppController, "AppController");
REGISTER_CLASS(PrintController, "PrintController"); REGISTER_CLASS(PrintController, "PrintController");
REGISTER_CLASS(PrintHost, "PrintHost");
SV* ConfigBase__as_hash(ConfigBase* THIS) SV* ConfigBase__as_hash(ConfigBase* THIS)
{ {
@ -133,7 +132,7 @@ SV* ConfigOption_to_SV(const ConfigOption &opt, const ConfigOptionDef &def)
auto optv = static_cast<const ConfigOptionPoints*>(&opt); auto optv = static_cast<const ConfigOptionPoints*>(&opt);
AV* av = newAV(); AV* av = newAV();
av_fill(av, optv->values.size()-1); av_fill(av, optv->values.size()-1);
for (const Pointf &v : optv->values) for (const Vec2d &v : optv->values)
av_store(av, &v - optv->values.data(), perl_to_SV_clone_ref(v)); av_store(av, &v - optv->values.data(), perl_to_SV_clone_ref(v));
return newRV_noinc((SV*)av); return newRV_noinc((SV*)av);
} }
@ -263,14 +262,14 @@ bool ConfigBase__set(ConfigBase* THIS, const t_config_option_key &opt_key, SV* v
return from_SV_check(value, &static_cast<ConfigOptionPoint*>(opt)->value); return from_SV_check(value, &static_cast<ConfigOptionPoint*>(opt)->value);
case coPoints: case coPoints:
{ {
std::vector<Pointf> &values = static_cast<ConfigOptionPoints*>(opt)->values; std::vector<Vec2d> &values = static_cast<ConfigOptionPoints*>(opt)->values;
AV* av = (AV*)SvRV(value); AV* av = (AV*)SvRV(value);
const size_t len = av_len(av)+1; const size_t len = av_len(av)+1;
values.clear(); values.clear();
values.reserve(len); values.reserve(len);
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0); SV** elem = av_fetch(av, i, 0);
Pointf point; Vec2d point(Vec2d::Zero());
if (elem == NULL || !from_SV_check(*elem, &point)) return false; if (elem == NULL || !from_SV_check(*elem, &point)) return false;
values.emplace_back(point); values.emplace_back(point);
} }
@ -509,7 +508,7 @@ void from_SV_check(SV* point_sv, Point* point)
} }
} }
SV* to_SV_pureperl(const Pointf* point) SV* to_SV_pureperl(const Vec2d* point)
{ {
AV* av = newAV(); AV* av = newAV();
av_fill(av, 1); av_fill(av, 1);
@ -518,23 +517,23 @@ SV* to_SV_pureperl(const Pointf* point)
return newRV_noinc((SV*)av); return newRV_noinc((SV*)av);
} }
bool from_SV(SV* point_sv, Pointf* point) bool from_SV(SV* point_sv, Vec2d* point)
{ {
AV* point_av = (AV*)SvRV(point_sv); AV* point_av = (AV*)SvRV(point_sv);
SV* sv_x = *av_fetch(point_av, 0, 0); SV* sv_x = *av_fetch(point_av, 0, 0);
SV* sv_y = *av_fetch(point_av, 1, 0); SV* sv_y = *av_fetch(point_av, 1, 0);
if (!looks_like_number(sv_x) || !looks_like_number(sv_y)) return false; if (!looks_like_number(sv_x) || !looks_like_number(sv_y)) return false;
*point = Pointf(SvNV(sv_x), SvNV(sv_y)); *point = Vec2d(SvNV(sv_x), SvNV(sv_y));
return true; return true;
} }
bool from_SV_check(SV* point_sv, Pointf* point) bool from_SV_check(SV* point_sv, Vec2d* point)
{ {
if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) {
if (!sv_isa(point_sv, perl_class_name(point)) && !sv_isa(point_sv, perl_class_name_ref(point))) if (!sv_isa(point_sv, perl_class_name(point)) && !sv_isa(point_sv, perl_class_name_ref(point)))
CONFESS("Not a valid %s object (got %s)", perl_class_name(point), HvNAME(SvSTASH(SvRV(point_sv)))); CONFESS("Not a valid %s object (got %s)", perl_class_name(point), HvNAME(SvSTASH(SvRV(point_sv))));
*point = *(Pointf*)SvIV((SV*)SvRV( point_sv )); *point = *(Vec2d*)SvIV((SV*)SvRV( point_sv ));
return true; return true;
} else { } else {
return from_SV(point_sv, point); return from_SV(point_sv, point);

47
xs/src/qhull/Announce.txt Normal file
View file

@ -0,0 +1,47 @@
Qhull 2015.2 2016/01/18
http://www.qhull.org
git@github.com:qhull/qhull.git
http://www.geomview.org
Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams,
furthest-site Voronoi diagrams, and halfspace intersections about a point.
It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm
for computing convex hulls. Qhull handles round-off errors from floating
point arithmetic. It can approximate a convex hull.
The program includes options for hull volume, facet area, partial hulls,
input transformations, randomization, tracing, multiple output formats, and
execution statistics. The program can be called from within your application.
You can view the results in 2-d, 3-d and 4-d with Geomview.
To download Qhull:
http://www.qhull.org/download
git@github.com:qhull/qhull.git
Download qhull-96.ps for:
Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The
Quickhull Algorithm for Convex Hulls," ACM Trans. on
Mathematical Software, 22(4):469-483, Dec. 1996.
http://www.acm.org/pubs/citations/journals/toms/1996-22-4/p469-barber/
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405
Abstract:
The convex hull of a set of points is the smallest convex set that contains
the points. This article presents a practical convex hull algorithm that
combines the two-dimensional Quickhull Algorithm with the general dimension
Beneath-Beyond Algorithm. It is similar to the randomized, incremental
algorithms for convex hull and Delaunay triangulation. We provide empirical
evidence that the algorithm runs faster when the input contains non-extreme
points, and that it uses less memory.
Computational geometry algorithms have traditionally assumed that input sets
are well behaved. When an algorithm is implemented with floating point
arithmetic, this assumption can lead to serious errors. We briefly describe
a solution to this problem when computing the convex hull in two, three, or
four dimensions. The output is a set of "thick" facets that contain all
possible exact convex hulls of the input. A variation is effective in five
or more dimensions.

128
xs/src/qhull/CMakeLists.txt Normal file
View file

@ -0,0 +1,128 @@
# This CMake file is written specifically to integrate qhull library with Slic3rPE
# (see https://github.com/prusa3d/Slic3r for more information about the project)
#
# Only original libraries qhullstatic_r and qhullcpp are included.
# They are built as a single statically linked library.
#
# Created by modification of the original qhull CMakeLists.
# Lukas Matena (25.7.2018), lukasmatena@seznam.cz
project(qhull)
cmake_minimum_required(VERSION 2.6)
# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c
set(qhull_VERSION "7.2.0") # Advance every release
#include(CMakeModules/CheckLFS.cmake)
#option(WITH_LFS "Enable Large File Support" ON)
#check_lfs(WITH_LFS)
message(STATUS "qhull Version: ${qhull_VERSION} (static linking)")
set(libqhull_HEADERS
# reentrant qhull HEADERS:
src/libqhull_r/libqhull_r.h
src/libqhull_r/geom_r.h
src/libqhull_r/io_r.h
src/libqhull_r/mem_r.h
src/libqhull_r/merge_r.h
src/libqhull_r/poly_r.h
src/libqhull_r/qhull_ra.h
src/libqhull_r/qset_r.h
src/libqhull_r/random_r.h
src/libqhull_r/stat_r.h
src/libqhull_r/user_r.h
# C++ interface to reentrant Qhull HEADERS:
src/libqhullcpp/Coordinates.h
src/libqhullcpp/functionObjects.h
src/libqhullcpp/PointCoordinates.h
src/libqhullcpp/Qhull.h
src/libqhullcpp/QhullError.h
src/libqhullcpp/QhullFacet.h
src/libqhullcpp/QhullFacetList.h
src/libqhullcpp/QhullFacetSet.h
src/libqhullcpp/QhullHyperplane.h
src/libqhullcpp/QhullIterator.h
src/libqhullcpp/QhullLinkedList.h
src/libqhullcpp/QhullPoint.h
src/libqhullcpp/QhullPoints.h
src/libqhullcpp/QhullPointSet.h
src/libqhullcpp/QhullQh.h
src/libqhullcpp/QhullRidge.h
src/libqhullcpp/QhullSet.h
src/libqhullcpp/QhullSets.h
src/libqhullcpp/QhullStat.h
src/libqhullcpp/QhullVertex.h
src/libqhullcpp/QhullVertexSet.h
src/libqhullcpp/RboxPoints.h
src/libqhullcpp/RoadError.h
src/libqhullcpp/RoadLogEvent.h
src/qhulltest/RoadTest.h
)
set(libqhull_SOURCES
# reentrant qhull SOURCES:
src/libqhull_r/global_r.c
src/libqhull_r/stat_r.c
src/libqhull_r/geom2_r.c
src/libqhull_r/poly2_r.c
src/libqhull_r/merge_r.c
src/libqhull_r/libqhull_r.c
src/libqhull_r/geom_r.c
src/libqhull_r/poly_r.c
src/libqhull_r/qset_r.c
src/libqhull_r/mem_r.c
src/libqhull_r/random_r.c
src/libqhull_r/usermem_r.c
src/libqhull_r/userprintf_r.c
src/libqhull_r/io_r.c
src/libqhull_r/user_r.c
src/libqhull_r/rboxlib_r.c
src/libqhull_r/userprintf_rbox_r.c
# C++ interface to reentrant Qhull SOURCES:
src/libqhullcpp/Coordinates.cpp
src/libqhullcpp/PointCoordinates.cpp
src/libqhullcpp/Qhull.cpp
src/libqhullcpp/QhullFacet.cpp
src/libqhullcpp/QhullFacetList.cpp
src/libqhullcpp/QhullFacetSet.cpp
src/libqhullcpp/QhullHyperplane.cpp
src/libqhullcpp/QhullPoint.cpp
src/libqhullcpp/QhullPointSet.cpp
src/libqhullcpp/QhullPoints.cpp
src/libqhullcpp/QhullQh.cpp
src/libqhullcpp/QhullRidge.cpp
src/libqhullcpp/QhullSet.cpp
src/libqhullcpp/QhullStat.cpp
src/libqhullcpp/QhullVertex.cpp
src/libqhullcpp/QhullVertexSet.cpp
src/libqhullcpp/RboxPoints.cpp
src/libqhullcpp/RoadError.cpp
src/libqhullcpp/RoadLogEvent.cpp
# headers for both (libqhullr and libqhullcpp:
${libqhull_HEADERS}
)
##################################################
# combined library (reentrant qhull and qhullcpp) for Slic3r:
set(qhull_STATIC qhull)
add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
set_target_properties(${qhull_STATIC} PROPERTIES
VERSION ${qhull_VERSION})
if(UNIX)
target_link_libraries(${qhull_STATIC} m)
endif(UNIX)
##################################################
# LIBDIR is defined in the main xs CMake file:
target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src)

38
xs/src/qhull/COPYING.txt Normal file
View file

@ -0,0 +1,38 @@
Qhull, Copyright (c) 1993-2015
C.B. Barber
Arlington, MA
and
The National Science and Technology Research Center for
Computation and Visualization of Geometric Structures
(The Geometry Center)
University of Minnesota
email: qhull@qhull.org
This software includes Qhull from C.B. Barber and The Geometry Center.
Qhull is copyrighted as noted above. Qhull is free software and may
be obtained via http from www.qhull.org. It may be freely copied, modified,
and redistributed under the following conditions:
1. All copyright notices must remain intact in all files.
2. A copy of this text file must be distributed along with any copies
of Qhull that you redistribute; this includes copies that you have
modified, or copies of programs or other software products that
include Qhull.
3. If you modify Qhull, you must include a notice giving the
name of the person performing the modification, the date of
modification, and the reason for such modification.
4. When distributing modified versions of Qhull, or other software
products that include Qhull, you must provide notice that the original
source code may be obtained as noted above.
5. There is no warranty or other guarantee of fitness for Qhull, it is
provided solely "as is". Bug reports or fixes may be sent to
qhull_bug@qhull.org; the authors may or may not act on them as
they desire.

623
xs/src/qhull/README.txt Normal file
View file

@ -0,0 +1,623 @@
This distribution of qhull library is only meant for interfacing qhull with Slic3rPE
(https://github.com/prusa3d/Slic3r).
The qhull source file was acquired from https://github.com/qhull/qhull at revision
f0bd8ceeb84b554d7cdde9bbfae7d3351270478c.
No changes to the qhull library were made, except for
- setting REALfloat=1 in user_r.h to enforce calculations in floats
- modifying CMakeLists.txt (the original was renamed to origCMakeLists.txt)
Many thanks to C. Bradford Barber and all contributors.
Lukas Matena (lukasmatena@seznam.cz)
25.7.2018
See original contents of the README file below.
======================================================================================
======================================================================================
======================================================================================
Name
qhull, rbox 2015.2 2016/01/18
Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection
Documentation:
html/index.htm
<http://www.qhull.org/html>
Available from:
<http://www.qhull.org>
<http://www.qhull.org/download>
<http://github.com/qhull/qhull> (git@github.com:qhull/qhull.git)
News and a paper:
<http://www.qhull.org/news>
<http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405>
Version 1 (simplicial only):
<http://www.qhull.org/download/qhull-1.0.tar.gz>
Purpose
Qhull is a general dimension convex hull program that reads a set
of points from stdin, and outputs the smallest convex set that contains
the points to stdout. It also generates Delaunay triangulations, Voronoi
diagrams, furthest-site Voronoi diagrams, and halfspace intersections
about a point.
Rbox is a useful tool in generating input for Qhull; it generates
hypercubes, diamonds, cones, circles, simplices, spirals,
lattices, and random points.
Qhull produces graphical output for Geomview. This helps with
understanding the output. <http://www.geomview.org>
Environment requirements
Qhull and rbox should run on all 32-bit and 64-bit computers. Use
an ANSI C or C++ compiler to compile the program. The software is
self-contained. It comes with examples and test scripts.
Qhull's C++ interface uses the STL. The C++ test program uses QTestLib
from the Qt Framework. Qhull's C++ interface may change without
notice. Eventually, it will move into the qhull shared library.
Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt
before using or distributing Qhull.
To cite Qhull, please use
Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull
algorithm for convex hulls," ACM Trans. on Mathematical Software,
22(4):469-483, Dec 1996, http://www.qhull.org.
To modify Qhull, particularly the C++ interface
Qhull is on GitHub
(http://github.com/qhull/qhull, git@github.com:qhull/qhull.git)
For internal documentation, see html/qh-code.htm
To install Qhull
Qhull is precompiled for Windows 32-bit, otherwise it needs compilation.
Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake,
.sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files
for Qt Creator. It compiles under Windows with mingw.
Install and build instructions follow.
See the end of this document for a list of distributed files.
-----------------
Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT
The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe,
qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files,
and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant
library while the other executables use the non-reentrant library.
To install Qhull:
- Unzip the files into a directory (e.g., named 'qhull')
- Click on QHULL-GO or open a command window into Qhull's bin directory.
- Test with 'rbox D4 | qhull'
To uninstall Qhull
- Delete the qhull directory
To learn about Qhull:
- Execute 'qconvex' for a synopsis and examples.
- Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points.
- Execute 'rbox 10 | qconvex i TO file' to write results to 'file'.
- Browse the documentation: qhull\html\index.htm
- If an error occurs, Windows sends the error to stdout instead of stderr.
Use 'TO xxx' to send normal output to xxx
To improve the command window
- Double-click the window bar to increase the size of the window
- Right-click the window bar
- Select Properties
- Check QuickEdit Mode
Select text with right-click or Enter
Paste text with right-click
- Change Font to Lucinda Console
- Change Layout to Screen Buffer Height 999, Window Size Height 55
- Change Colors to Screen Background White, Screen Text Black
- Click OK
- Select 'Modify shortcut that started this window', then OK
If you use qhull a lot, install a bash shell such as
MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash),
or Cygwin (www.cygwin.com).
-----------------
Installing Qhull on Unix with gcc
To build Qhull, static libraries, shared library, and C++ interface
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- make
- export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
The Makefiles may be edited for other compilers.
If 'testqset' exits with an error, qhull is broken
A simple Makefile for Qhull is in src/libqhull and src/libqhull_r.
To build the Qhull executables and libqhullstatic
- Extract Qhull from qhull...tgz or qhull...zip
- cd src/libqhull_r # cd src/libqhull
- make
-----------------
Installing Qhull with CMake 2.6 or later
See CMakeLists.txt for examples and further build instructions
To build Qhull, static libraries, shared library, and C++ interface
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- cd build
- cmake --help # List build generators
- make -G "<generator>" .. && cmake ..
- cmake ..
- make
- make install
The ".." is important. It refers to the parent directory (i.e., qhull/)
On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators
have a "Win64" tag.
If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in
If cmake fails with "No CMAKE_C_COMPILER could be found"
- cmake was not able to find the build environment specified by -G "..."
-----------------
Installing Qhull with Qt
To build Qhull, including its C++ test (qhulltest)
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Load src/qhull-all.pro into QtCreator
- Build
-------------------
Working with Qhull's C++ interface
See html/qh-code.htm#cpp for calling Qhull from C++ programs
See html/qh-code.htm#reentrant for converting from Qhull-2012
Examples of using the C++ interface
user_eg3_r.cpp
qhulltest/*_test.cpp
Qhull's C++ interface is likely to change. Stay current with GitHub.
To clone Qhull's next branch from http://github.com/qhull/qhull
git init
git clone git@github.com:qhull/qhull.git
cd qhull
git checkout next
...
git pull origin next
Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries
use the C routines setjmp() and longjmp() for error handling. They must
be compiled with the same compiler.
-------------------
Calling Qhull from C programs
See html/qh-code.htm#library for calling Qhull from C programs
See html/qh-code.htm#reentrant for converting from Qhull-2012
Warning: You will need to understand Qhull's data structures and read the
code. Most users will find it easier to call Qhull as an external command.
The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT
to most Qhull routines. This allows multiple instances of Qhull to run
at the same time. It simplifies the C++ interface.
The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to
Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr').
This allows the same code to use static memory or heap memory.
If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT;
otherwise qh_qh is a global static data structure of type qhT.
------------------
Compiling Qhull with Microsoft Visual C++
To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Load solution build/qhull-32.sln
- Build target 'Win32'
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012)
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later
- 64-bit Qhull has larger data structures due to 64-bit pointers
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Load solution build/qhull-64.sln
- Build target 'Win32'
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64)
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
To compile Qhull with Microsoft Visual C++ 2005 (vcproj files)
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Load solution build/qhull.sln
- Build target 'win32' (not 'x64')
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4)
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
-----------------
Compiling Qhull with Qt Creator
Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh
Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/)
To compile Qhull with Qt Creator
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Download the Qt SDK
- Start Qt Creator
- Load src/qhull-all.pro
- Build
-----------------
Compiling Qhull with mingw on Windows
To compile Qhull with MINGW
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Install Road Bash (http://www.qhull.org/bash)
or install MSYS (http://www.mingw.org/wiki/msys)
- Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64).
Mingw is included with Qt SDK.
- make
-----------------
Compiling Qhull with cygwin on Windows
To compile Qhull with cygwin
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
- Install cygwin (http://www.cygwin.com)
- Include packages for gcc, make, ar, and ln
- make
-----------------
Compiling from Makfile without gcc
The file, qhull-src.tgz, contains documentation and source files for
qhull and rbox.
To unpack the tgz file
- tar zxf qhull-src.tgz
- cd qhull
- Use qhull/Makefile
Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile
Compiling qhull and rbox with Makefile
- in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines
- the defaults are gcc and enscript
- CCOPTS1 should include the ANSI flag. It defines __STDC__
- in user.h, check the definitions of qh_SECticks and qh_CPUclock.
- use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour
- type: make
- this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a
- type: make doc
- this prints the man page
- See also qhull/html/index.htm
- if your compiler reports many errors, it is probably not a ANSI C compiler
- you will need to set the -ansi switch or find another compiler
- if your compiler warns about missing prototypes for fprintf() etc.
- this is ok, your compiler should have these in stdio.h
- if your compiler warns about missing prototypes for memset() etc.
- include memory.h in qhull_a.h
- if your compiler reports "global.c: storage size of 'qh_qh' isn't known"
- delete the initializer "={0}" in global.c, stat.c and mem.c
- if your compiler warns about "stat.c: improper initializer"
- this is ok, the initializer is not used
- if you have trouble building libqhull.a with 'ar'
- try 'make -f Makefile.txt qhullx'
- if the code compiles, the qhull test case will automatically execute
- if an error occurs, there's an incompatibility between machines
- If you can, try a different compiler
- You can turn off the Qhull memory manager with qh_NOmem in mem.h
- You can turn off compiler optimization (-O2 in Makefile)
- If you find the source of the problem, please let us know
- to install the programs and their man pages:
- define MANDIR and BINDIR
- type 'make install'
- if you have Geomview (www.geomview.org)
- try 'rbox 100 | qconvex G >a' and load 'a' into Geomview
- run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm)
------------------
Compiling on other machines and compilers
Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included.
Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set
the ANSI option in Options:Project:Compiler:Source:Language-compliance.
Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack.
Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with
Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c".
Use the same options for Qhull 1.0. [D. Zwick]
If you have troubles with the memory manager, you can turn it off by
defining qh_NOmem in mem.h.
-----------------
Distributed files
README.txt // Instructions for installing Qhull
REGISTER.txt // Qhull registration
COPYING.txt // Copyright notice
QHULL-GO.lnk // Windows icon for eg/qhull-go.bat
Announce.txt // Announcement
CMakeLists.txt // CMake build file (2.6 or later)
CMakeModules/CheckLFS.cmake // enables Large File Support in cmake
File_id.diz // Package descriptor
index.htm // Home page
Makefile // Makefile for gcc and other compilers
qhull*.md5sum // md5sum for all files
bin/* // Qhull executables and dll (.zip only)
build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p
build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later)
build/*-32.vcxproj
build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later)
build/*-64.vcxproj
build/qhull.sln // DevStudio solution and project files (2005 and 2009)
build/*.vcproj
eg/* // Test scripts and geomview files from q_eg
html/index.htm // Manual
html/qh-faq.htm // Frequently asked questions
html/qh-get.htm // Download page
html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road)
src/Changes.txt // Change history for Qhull and rbox
src/qhull-all.pro // Qt project
eg/
q_eg // shell script for Geomview examples (eg.01.cube)
q_egtest // shell script for Geomview test examples
q_test // shell script to test qhull
q_test-ok.txt // output from q_test
qhulltest-ok.txt // output from qhulltest (Qt only)
make-vcproj.sh // bash shell script to create vcproj and vcxprog files
qhull-zip.sh // bash shell script for distribution files
rbox consists of (bin, html):
rbox.exe // Win32 executable (.zip only)
rbox.htm // html manual
rbox.man // Unix man page
rbox.txt
qhull consists of (bin, html):
qconvex.exe // Win32 executables and dlls (.zip download only)
qhull.exe // Built with the reentrant library (about 2% slower)
qdelaunay.exe
qhalf.exe
qvoronoi.exe
qhull_r.dll
qhull-go.bat // command window
qconvex.htm // html manual
qdelaun.htm
qdelau_f.htm
qhalf.htm
qvoronoi.htm
qvoron_f.htm
qh-eg.htm
qh-code.htm
qh-impre.htm
index.htm
qh-opt*.htm
qh-quick.htm
qh--*.gif // images for manual
normal_voronoi_knauss_oesterle.jpg
qhull.man // Unix man page
qhull.txt
bin/
msvcr80.dll // Visual C++ redistributable file (.zip download only)
src/
qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a
rbox/rbox.c
qconvex/qconvex.c
qhalf/qhalf.c
qdelaunay/qdelaunay.c
qvoronoi/qvoronoi.c
qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a
rbox/rbox_r.c
qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile
qhalf/qhalf_r.c
qdelaunay/qdelaun_r.c
qvoronoi/qvoronoi_r.c
user_eg/user_eg_r.c // example of using qhull_r.dll from a user program
user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program
user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a
qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib
qhull-*.pri // Include files for Qt projects
testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c
testqset/testqset.c // Test of non-rentrant qset.c and mem.c
src/libqhull
libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll)
index.htm // design documentation for libqhull
qh-*.htm
qhull-exports.def // Export Definition file for Visual C++
Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
Mborland // Makefile for Borland C++ 5.0
libqhull.h // header file for qhull
user.h // header file of user definable constants
libqhull.c // Quickhull algorithm with partitioning
user.c // user re-definable functions
usermem.c
userprintf.c
userprintf_rbox.c
qhull_a.h // include files for libqhull/*.c
geom.c // geometric routines
geom2.c
geom.h
global.c // global variables
io.c // input-output routines
io.h
mem.c // memory routines, this is stand-alone code
mem.h
merge.c // merging of non-convex facets
merge.h
poly.c // polyhedron routines
poly2.c
poly.h
qset.c // set routines, this only depends on mem.c
qset.h
random.c // utilities w/ Park & Miller's random number generator
random.h
rboxlib.c // point set generator for rbox
stat.c // statistics
stat.h
src/libqhull_r
libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll)
index.htm // design documentation for libqhull_r
qh-*_r.htm
qhull-exports_r.def // Export Definition file for Visual C++
Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
libqhull_r.h // header file for qhull
user_r.h // header file of user definable constants
libqhull_r.c // Quickhull algorithm wi_r.hpartitioning
user_r.c // user re-definable functions
usermem.c
userprintf.c
userprintf_rbox.c
qhull_ra.h // include files for libqhull/*_r.c
geom_r.c // geometric routines
geom2.c
geom_r.h
global_r.c // global variables
io_r.c // input-output routines
io_r.h
mem_r.c // memory routines, this is stand-alone code
mem.h
merge_r.c // merging of non-convex facets
merge.h
poly_r.c // polyhedron routines
poly2.c
poly_r.h
qset_r.c // set routines, this only depends on mem_r.c
qset.h
random_r.c // utilities w/ Park & Miller's random number generator
random.h
rboxlib_r.c // point set generator for rbox
stat_r.c // statistics
stat.h
src/libqhullcpp/
libqhullcpp.pro // Qt project for renentrant, static C++ library
Qhull.cpp // Calls libqhull_r.c from C++
Qhull.h
qt-qhull.cpp // Supporting methods for Qt
Coordinates.cpp // input classes
Coordinates.h
PointCoordinates.cpp
PointCoordinates.h
RboxPoints.cpp // call rboxlib.c from C++
RboxPoints.h
QhullFacet.cpp // data structure classes
QhullFacet.h
QhullHyperplane.cpp
QhullHyperplane.h
QhullPoint.cpp
QhullPoint.h
QhullQh.cpp
QhullRidge.cpp
QhullRidge.h
QhullVertex.cpp
QhullVertex.h
QhullFacetList.cpp // collection classes
QhullFacetList.h
QhullFacetSet.cpp
QhullFacetSet.h
QhullIterator.h
QhullLinkedList.h
QhullPoints.cpp
QhullPoints.h
QhullPointSet.cpp
QhullPointSet.h
QhullSet.cpp
QhullSet.h
QhullSets.h
QhullVertexSet.cpp
QhullVertexSet.h
functionObjects.h // supporting classes
QhullError.cpp
QhullError.h
QhullQh.cpp
QhullQh.h
QhullStat.cpp
QhullStat.h
RoadError.cpp // Supporting base classes
RoadError.h
RoadLogEvent.cpp
RoadLogEvent.h
usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error
src/libqhullstatic/
libqhullstatic.pro // Qt project for non-reentrant, static library
src/libqhullstatic_r/
libqhullstatic_r.pro // Qt project for reentrant, static library
src/qhulltest/
qhulltest.pro // Qt project for test of C++ interface
Coordinates_test.cpp // Test of each class
PointCoordinates_test.cpp
Qhull_test.cpp
QhullFacet_test.cpp
QhullFacetList_test.cpp
QhullFacetSet_test.cpp
QhullHyperplane_test.cpp
QhullLinkedList_test.cpp
QhullPoint_test.cpp
QhullPoints_test.cpp
QhullPointSet_test.cpp
QhullRidge_test.cpp
QhullSet_test.cpp
QhullVertex_test.cpp
QhullVertexSet_test.cpp
RboxPoints_test.cpp
RoadTest.cpp // Run multiple test files with QTestLib
RoadTest.h
-----------------
Authors:
C. Bradford Barber Hannu Huhdanpaa (Version 1.0)
bradb@shore.net hannu@qhull.org
Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161
and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard
University. If you find Qhull useful, please let us know.

32
xs/src/qhull/REGISTER.txt Normal file
View file

@ -0,0 +1,32 @@
Dear Qhull User
We would like to find out how you are using our software. Think of
Qhull as a new kind of shareware: you share your science and successes
with us, and we share our software and support with you.
If you use Qhull, please send us a note telling
us what you are doing with it.
We need to know:
(1) What you are working on - an abstract of your work would be
fine.
(2) How Qhull has helped you, for example, by increasing your
productivity or allowing you to do things you could not do
before. If Qhull had a direct bearing on your work, please
tell us about this.
We encourage you to cite Qhull in your publications.
To cite Qhull, please use
Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull
algorithm for convex hulls," ACM Trans. on Mathematical Software,
22(4):469-483, Dec 1996, http://www.qhull.org.
Please send e-mail to
bradb@shore.net
Thank you!

935
xs/src/qhull/html/index.htm Normal file
View file

@ -0,0 +1,935 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Microsoft FrontPage 2.0">
<title>Qhull manual</title>
<!-- Navigation links
NOTE -- verify all links by 'grep href=' 'grep name=' add # 'sort /+7'
index.htm
-->
</head>
<body>
<p><a name="TOP"><b>Up:</b></a> <a
href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b><a
href="http://www.qhull.org/news">News</a> about Qhull<br>
<b>Up:</b> <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> about Qhull<br>
<b>To:</b> <a href="#TOC">Qhull manual: Table of Contents</a>
(please wait while loading) <br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/fixed.html"><img
src="qh--rand.gif" alt="[random-fixed]" align="middle"
width="100" height="100"></a> Qhull manual </h1>
<p>Qhull is a general dimension code for computing convex hulls,
Delaunay triangulations, halfspace intersections about a point, Voronoi
diagrams, furthest-site Delaunay triangulations, and
furthest-site Voronoi diagrams. These structures have
applications in science, engineering, statistics, and
mathematics. See <a
href="http://www.cs.mcgill.ca/~fukuda/soft/polyfaq/polyfaq.html">Fukuda's
introduction</a> to convex hulls, Delaunay triangulations,
Voronoi diagrams, and linear programming. For a detailed
introduction, see O'Rourke [<a href="#orou94">'94</a>], <i>Computational
Geometry in C</i>.
</p>
<p>There are six programs. Except for rbox, they use
the same code. Each program includes instructions and examples.
<blockquote>
<ul>
<li><a href="qconvex.htm">qconvex</a> -- convex hulls
<li><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulations and
furthest-site Delaunay triangulations
<li><a href="qhalf.htm">qhalf</a> -- halfspace intersections about a point
<li><a href="qhull.htm">qhull</a> -- all structures with additional options
<li><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagrams and
furthest-site Voronoi diagrams
<li><a href="rbox.htm">rbox</a> -- generate point distributions for qhull
</ul>
</blockquote>
<p>Qhull implements the Quickhull algorithm for computing the
convex hull. Qhull includes options
for hull volume, facet area, multiple output formats, and
graphical output. It can approximate a convex hull. </p>
<p>Qhull handles roundoff errors from floating point
arithmetic. It generates a convex hull with "thick" facets.
A facet's outer plane is clearly above all of the points;
its inner plane is clearly below the facet's vertices. Any
exact convex hull must lie between the inner and outer plane.
<p>Qhull uses merged facets, triangulated output, or joggled
input. Triangulated output triangulates non-simplicial, merged
facets. Joggled input also
guarantees simplicial output, but it
is less accurate than merged facets. For merged facets, Qhull
reports the maximum outer and inner plane.
<p><i>Brad Barber, Arlington, MA</i></p>
<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h2><a href="#TOP">&#187;</a><a name="TOC">Qhull manual: Table of
Contents </a></h2>
<ul>
<li><a href="#when">When</a> to use Qhull
<ul>
<li><a href="http://www.qhull.org/news">News</a> for Qhull
with new features and reported bugs.
<li><a href="http://www.qhull.org">Home</a> for Qhull with additional URLs
(<a href=index.htm>local copy</a>)
<li><a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> for Qhull (<a href="qh-faq.htm">local copy</a>)
<li><a href="http://www.qhull.org/download">Download</a> Qhull (<a href=qh-get.htm>local copy</a>)
<li><a href="qh-quick.htm#programs">Quick</a> reference for Qhull and its <a href="qh-quick.htm#options">options</a>
<p>
<li><a href="../COPYING.txt">COPYING.txt</a> - copyright notice<br>
<li><a href="../REGISTER.txt">REGISTER.txt</a> - registration<br>
<li><a href="../README.txt">README.txt</a> - installation
instructions<br>
<li><a href="../src/Changes.txt">Changes.txt</a> - change history <br>
<li><a href="qhull.txt">qhull.txt</a> - Unix manual page
</ul>
<p>
<li><a href="#description">Description</a> of Qhull
<ul>
<li><a href="#definition">de</a>finition &#149; <a
href="#input">in</a>put &#149; <a href="#output">ou</a>tput
&#149; <a href="#algorithm">al</a>gorithm &#149; <a
href="#structure">da</a>ta structure </li>
<li><a href="qh-impre.htm">Imprecision</a> in Qhull</li>
<li><a href="qh-impre.htm#joggle">Merged facets</a> or joggled input
<li><a href="qh-eg.htm">Examples</a> of Qhull</li>
</ul>
<p>
<li><a href=qh-quick.htm#programs>Qhull programs</a>, with instructions and examples
<ul>
<li><a href="qconvex.htm">qconvex</a> -- convex hulls
<li><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulations and
furthest-site Delaunay triangulations
<li><a href="qhalf.htm">qhalf</a> -- halfspace intersections about a point
<li><a href="qhull.htm">qhull</a> -- all structures with additional options
<li><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagrams and
furthest-site Voronoi diagrams
<li><a href="rbox.htm">rbox</a> -- generate point distributions for qhull
</ul>
<p>
<li><a href="qh-quick.htm#options">Qhull options</a><ul>
<li><a href="qh-opto.htm#output">Output</a> formats</li>
<li><a href="qh-optf.htm#format">Additional</a> I/O
formats</li>
<li><a href="qh-optg.htm#geomview">Geomview</a>
output options</li>
<li><a href="qh-optp.htm#print">Print</a> options</li>
<li><a href="qh-optq.htm#qhull">Qhull</a> control
options</li>
<li><a href="qh-optc.htm#prec">Precision</a> options</li>
<li><a href="qh-optt.htm#trace">Trace</a> options</li>
</ul>
</li>
<p>
<li><a href="#geomview">Geomview</a>, Qhull's graphical viewer</li>
<ul>
<li><a href="#geomview-install">Installing Geomview</a></li>
<li><a href="#geomview-use">Using Geomview</a></li>
<li><a href="#geomview-win">Building Geomview for Windows</a></li>
</ul>
<p>
<li><a href="qh-code.htm">Qhull internals</a><ul>
<li><a href="qh-code.htm#reentrant">Reentrant</a> Qhull</li>
<li><a href="qh-code.htm#convert">How to convert</a> code to reentrant Qhull</li>
<li><a href="qh-code.htm#64bit">Qhull</a> on 64-bit computers</li>
<li><a href="qh-code.htm#cpp">Calling</a> Qhull
from C++ programs</li>
<li><a href="qh-code.htm#library">Calling</a> Qhull
from C programs</li>
<li><a href="qh-code.htm#performance">Performance</a>
of Qhull</li>
<li><a href="qh-code.htm#enhance">Enhancements</a> to
Qhull</li>
<li><a href="../src/libqhull_r/index.htm">Reentrant</a> Qhull functions, macros, and
data structures </li>
<li><a href="../src/libqhull/index.htm">Qhull</a> functions, macros, and
data structures </li>
</ul>
</li>
<p>
<li>Related URLs
<ul>
<li><a href="news:comp.graphics.algorithms">Newsgroup</a>:
comp.graphics.algorithms
<li><a
href="http://www.faqs.org/faqs/graphics/algorithms-faq/">FAQ</a> for computer graphics algorithms and
Exaflop's <a href="http://exaflop.org/docs/cgafaq/cga6.html">geometric</a> structures.
<li>Amenta's <a href="http://www.geom.uiuc.edu/software/cglist">Directory
of Computational Geometry Software </a></li>
<li>Erickson's <a
href="http://compgeom.cs.uiuc.edu/~jeffe/compgeom/code.html">Computational
Geometry Software</a> </li>
<li>Fukuda's <a
href="http://www.cs.mcgill.ca/~fukuda/soft/polyfaq/polyfaq.html">
introduction</a> to convex hulls, Delaunay triangulations,
Voronoi diagrams, and linear programming.
<li>Stony Brook's <a
href="http://www.cs.sunysb.edu/~algorith/major_section/1.6.shtml">Algorithm Repository</a> on computational geometry.
</li>
</ul>
<p>
<li><a href="#bugs">What to do</a> if something goes wrong</li>
<li><a href="#email">Email</a></li>
<li><a href="#authors">Authors</a></li>
<li><a href="#ref">References</a></li>
<li><a href="#acknowledge">Acknowledgments</a></li>
</ul>
<h2><a href="#TOC">&#187;</a><a name="when">When to use Qhull</a></h2>
<blockquote>
<p>Qhull constructs convex hulls, Delaunay triangulations,
halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay
triangulations, and furthest-site Voronoi diagrams.</p>
<p>For convex hulls and halfspace intersections, Qhull may be used
for 2-d upto 8-d. For Voronoi diagrams and Delaunay triangulations, Qhull may be
used for 2-d upto 7-d. In higher dimensions, the size of the output
grows rapidly and Qhull does not work well with virtual memory.
If <i>n</i> is the size of
the input and <i>d</i> is the dimension (d>=3), the size of the output
and execution time
grows by <i>n^(floor(d/2)</i>
[see <a href=qh-code.htm#performance>Performance</a>]. For example, do
not try to build a 16-d convex hull of 1000 points. It will
have on the order of 1,000,000,000,000,000,000,000,000 facets.
<p>On a 600 MHz Pentium 3, Qhull computes the 2-d convex hull of
300,000 cocircular points in 11 seconds. It computes the
2-d Delaunay triangulation and 3-d convex hull of 120,000 points
in 12 seconds. It computes the
3-d Delaunay triangulation and 4-d convex hull of 40,000 points
in 18 seconds. It computes the
4-d Delaunay triangulation and 5-d convex hull of 6,000 points
in 12 seconds. It computes the
5-d Delaunay triangulation and 6-d convex hull of 1,000 points
in 12 seconds. It computes the
6-d Delaunay triangulation and 7-d convex hull of 300 points
in 15 seconds. It computes the
7-d Delaunay triangulation and 8-d convex hull of 120 points
in 15 seconds. It computes the
8-d Delaunay triangulation and 9-d convex hull of 70 points
in 15 seconds. It computes the
9-d Delaunay triangulation and 10-d convex hull of 50 points
in 17 seconds. The 10-d convex hull of 50 points has about 90,000 facets.
<!-- duplicated in index.htm and html/index.htm -->
<p>Qhull does <i>not</i> support constrained Delaunay
triangulations, triangulation of non-convex surfaces, mesh
generation of non-convex objects, or medium-sized inputs in 9-D
and higher. </p>
<p>This is a big package with many options. It is one of the
fastest available. It is the only 3-d code that handles precision
problems due to floating point arithmetic. For example, it
implements the identity function for extreme points (see <a
href="qh-impre.htm">Imprecision in Qhull</a>). </p>
<p>[2016] A newly discovered, bad case for Qhull is multiple, nearly incident points within a 10^-13 ball of 3-d and higher
Delaunay triangulations (input sites in the unit cube). Nearly incident points within substantially
smaller or larger balls are OK. Error QH6271 is reported if a problem occurs. A future release of Qhull
will handle this case. For more information, see "Nearly coincident points on an edge" in <a href="../html/qh-impre.htm#limit">Limitations of merged facets</a>
<p>If you need a short code for convex hull, Delaunay
triangulation, or Voronoi volumes consider Clarkson's <a
href="http://www.netlib.org/voronoi/hull.html">hull
program</a>. If you need 2-d Delaunay triangulations consider
Shewchuk's <a href="http://www.cs.cmu.edu/~quake/triangle.html">triangle
program</a>. It is much faster than Qhull and it allows
constraints. Both programs use exact arithmetic. They are in <a
href="http://www.netlib.org/voronoi/">http://www.netlib.org/voronoi/</a>.
<p>If your input is in general position (i.e., no coplanar or colinear points),
<li><a href="https://github.com/tomilov/quickhull/blob/master/include/quickhull.hpp">Tomilov's quickhull.hpp</a> (<a href"http://habrahabr.ru/post/245221/"documentation-ru</a/>)
or Qhull <a
href="http://www.qhull.org/download">version
1.0</a> may meet your needs. Both programs detect precision problems,
but do not handle them.</p>
<p><a href=http://www.cgal.org>CGAL</a> is a library of efficient and reliable
geometric algorithms. It uses C++ templates and the Boost library to produce dimension-specific
code. This allows more efficient use of memory than Qhull's general-dimension
code. CGAL simulates arbitrary precision while Qhull handles round-off error
with thick facets. Compare the two approaches with <a href="http://doc.cgal.org/latest/Manual/devman_robustness.html">Robustness Issues in CGAL</a>,
and <a href+"qh-impre.htm">Imprecision in Qhull</a>.
<p><a href=http://www.algorithmic-solutions.com/enleda.htm>Leda</a> is a
library for writing computational
geometry programs and other combinatorial algorithms. It
includes routines for computing 3-d convex
hulls, 2-d Delaunay triangulations, and 3-d Delaunay triangulations.
It provides rational arithmetic and graphical output. It runs on most
platforms.
<p>If your problem is in high dimensions with a few,
non-simplicial facets, try Fukuda's <a
href="http://www.cs.mcgill.ca/~fukuda/soft/cdd_home/cdd.html">cdd</a>.
It is much faster than Qhull for these distributions. </p>
<p>Custom software for 2-d and 3-d convex hulls may be faster
than Qhull. Custom software should use less memory. Qhull uses
general-dimension data structures and code. The data structures
support non-simplicial facets.</p>
<p>Qhull is not suitable for mesh generation or triangulation of
arbitrary surfaces. You may use Qhull if the surface is convex or
completely visible from an interior point (e.g., a star-shaped
polyhedron). First, project each site to a sphere that is
centered at the interior point. Then, compute the convex hull of
the projected sites. The facets of the convex hull correspond to
a triangulation of the surface. For mesh generation of arbitrary
surfaces, see <a
href="http://www.robertschneiders.de/meshgeneration/meshgeneration.html">Schneiders'
Finite Element Mesh Generation</a>.</p>
<p>Qhull is not suitable for constrained Delaunay triangulations.
With a lot of work, you can write a program that uses Qhull to
add constraints by adding additional points to the triangulation.</p>
<p>Qhull is not suitable for the subdivision of arbitrary
objects. Use <tt>qdelaunay</tt> to subdivide a convex object.</p>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="description">Description of
Qhull </a></h2>
<blockquote>
<h3><a href="#TOC">&#187;</a><a name="definition">definition</a></h3>
<blockquote>
<p>The <i>convex hull</i> of a point set <i>P</i> is the smallest
convex set that contains <i>P</i>. If <i>P</i> is finite, the
convex hull defines a matrix <i>A</i> and a vector <i>b</i> such
that for all <i>x</i> in <i>P</i>, <i>Ax+b &lt;= [0,...]</i>. </p>
<p>Qhull computes the convex hull in 2-d, 3-d, 4-d, and higher
dimensions. Qhull represents a convex hull as a list of facets.
Each facet has a set of vertices, a set of neighboring facets,
and a halfspace. A halfspace is defined by a unit normal and an
offset (i.e., a row of <i>A</i> and an element of <i>b</i>). </p>
<p>Qhull accounts for round-off error. It returns
&quot;thick&quot; facets defined by two parallel hyperplanes. The
outer planes contain all input points. The inner planes exclude
all output vertices. See <a href="qh-impre.htm#imprecise">Imprecise
convex hulls</a>.</p>
<p>Qhull may be used for the Delaunay triangulation or the
Voronoi diagram of a set of points. It may be used for the
intersection of halfspaces. </p>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="input">input format</a></h3>
<blockquote>
<p>The input data on <tt>stdin</tt> consists of:</p>
<ul>
<li>first line contains the dimension</li>
<li>second line contains the number of input points</li>
<li>remaining lines contain point coordinates</li>
</ul>
<p>For example: </p>
<pre>
3 #sample 3-d input
5
0.4 -0.5 1.0
1000 -1e-5 -100
0.3 0.2 0.1
1.0 1.0 1.0
0 0 0
</pre>
<p>Input may be entered by hand. End the input with a control-D
(^D) character. </p>
<p>To input data from a file, use I/O redirection or '<a
href="qh-optt.htm#TI">TI file</a>'. The filename may not
include spaces or quotes.</p>
<p>A comment starts with a non-numeric character and continues to
the end of line. The first comment is reported in summaries and
statistics. With multiple <tt>qhull</tt> commands, use option '<a
href="qh-optf.htm#FQ">FQ</a>' to place a comment in the output.</p>
<p>The dimension and number of points can be reversed. Comments
and line breaks are ignored. Error reporting is better if there
is one point per line.</p>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="option">option format</a></h3>
<blockquote>
<p>Use options to specify the output formats and control
Qhull. The <tt>qhull</tt> program takes all options. The
other programs use a subset of the options. They disallow
experimental and inappropriate options.
<blockquote>
<ul>
<li>
qconvex == qhull
<li>
qdelaunay == qhull d Qbb
<li>
qhalf == qhull H
<li>
qvoronoi == qhull v Qbb
</ul>
</blockquote>
<p>Single letters are used for output formats and precision
constants. The other options are grouped into menus for formats
('<a href="qh-optf.htm#format">F</a>'), Geomview ('<a
href="qh-optg.htm#geomview">G </a>'), printing ('<a
href="qh-optp.htm#print">P</a>'), Qhull control ('<a
href="qh-optq.htm#qhull">Q </a>'), and tracing ('<a
href="qh-optt.htm#trace">T</a>'). The menu options may be listed
together (e.g., 'GrD3' for 'Gr' and 'GD3'). Options may be in any
order. Capitalized options take a numeric argument (except for '<a
href="qh-optp.htm#PG">PG</a>' and '<a href="qh-optf.htm#format">F</a>'
options). Use option '<a href="qh-optf.htm#FO">FO</a>' to print
the selected options.</p>
<p>Qhull uses zero-relative indexing. If there are <i>n</i>
points, the index of the first point is <i>0</i> and the index of
the last point is <i>n-1</i>.</p>
<p>The default options are:</p>
<ul>
<li>summary output ('<a href="qh-opto.htm#s">s</a>') </li>
<li>merged facets ('<a href="qh-optc.htm#C0">C-0</a>' in 2-d,
3-d, 4-d; '<a href="qh-optq.htm#Qx">Qx</a>' in 5-d and
up)</li>
</ul>
<p>Except for bounding box
('<a href="qh-optq.htm#Qbk">Qbk:n</a>', etc.), drop facets
('<a href="qh-optp.htm#Pdk">Pdk:n</a>', etc.), and
Qhull command ('<a href="qh-optf.htm#FQ">FQ</a>'), only the last
occurence of an option counts.
Bounding box and drop facets may be repeated for each dimension.
Option 'FQ' may be repeated any number of times.
<p>The Unix <tt>tcsh</tt> and <tt>ksh </tt>shells make it easy to
try out different options. In Windows 95, use a command window with <tt>doskey</tt>
and a window scroller (e.g., <tt>peruse</tt>). </p>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="output">output format</a></h3>
<blockquote>
<p>To write the results to a file, use I/O redirection or '<a
href="qh-optt.htm#TO">TO file</a>'. Windows 95 users should use
'TO file' or the console. If a filename is surrounded by single quotes,
it may include spaces.
</p>
<p>The default output option is a short summary ('<a
href="qh-opto.htm#s">s</a>') to <tt>stdout</tt>. There are many
others (see <a href="qh-opto.htm">output</a> and <a
href="qh-optf.htm">formats</a>). You can list vertex incidences,
vertices and facets, vertex coordinates, or facet normals. You
can view Qhull objects with Geomview, Mathematica, or Maple. You can
print the internal data structures. You can call Qhull from your
application (see <a href="qh-code.htm#library">Qhull library</a>).</p>
<p>For example, 'qhull <a href="qh-opto.htm#o">o</a>' lists the
vertices and facets of the convex hull. </p>
<p>Error messages and additional summaries ('<a
href="qh-opto.htm#s">s</a>') go to <tt>stderr</tt>. Unless
redirected, <tt>stderr</tt> is the console.</p>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="algorithm">algorithm</a></h3>
<blockquote>
<p>Qhull implements the Quickhull algorithm for convex hull
[Barber et al. <a href="#bar-dob96">'96</a>]. This algorithm
combines the 2-d Quickhull algorithm with the <em>n</em>-d
beneath-beyond algorithm [c.f., Preparata &amp; Shamos <a
href="#pre-sha85">'85</a>]. It is similar to the randomized
algorithms of Clarkson and others [Clarkson &amp; Shor <a
href="#cla-sho89">'89</a>; Clarkson et al. <a href="#cla-meh93">'93</a>;
Mulmuley <a href="#mulm94">'94</a>]. For a demonstration, see <a
href="qh-eg.htm#how">How Qhull adds a point</a>. The main
advantages of Quickhull are output sensitive performance (in
terms of the number of extreme points), reduced space
requirements, and floating-point error handling. </p>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="structure">data structures</a></h3>
<blockquote>
<p>Qhull produces the following data structures for dimension <i>d</i>:
</p>
<ul>
<li>A <em>coordinate</em> is a real number in floating point
format. </li>
<li>A <em>point</em> is an array of <i>d</i> coordinates.
With option '<a href="qh-optq.htm#QJn">QJ</a>', the
coordinates are joggled by a small amount. </li>
<li>A <em>vertex</em> is an input point. </li>
<li>A <em>hyperplane</em> is <i>d</i> normal coefficients and
an offset. The length of the normal is one. The
hyperplane defines a halfspace. If <i>V</i> is a normal, <i>b</i>
is an offset, and <i>x</i> is a point inside the convex
hull, then <i>Vx+b &lt;0</i>.</li>
<li>An <em>outer plane</em> is a positive
offset from a hyperplane. When Qhull is done, all points
will be below all outer planes.</li>
<li>An <em>inner plane</em> is a negative
offset from a hyperplane. When Qhull is done, all
vertices will be above the corresponding inner planes.</li>
<li>An <em>orientation</em> is either 'top' or 'bottom'. It is the
topological equivalent of a hyperplane's geometric
orientation. </li>
<li>A <em>simplicial facet</em> is a set of
<i>d</i> neighboring facets, a set of <i>d</i> vertices, a
hyperplane equation, an inner plane, an outer plane, and
an orientation. For example in 3-d, a simplicial facet is
a triangle. </li>
<li>A <em>centrum</em> is a point on a facet's hyperplane. A
centrum is the average of a facet's vertices. Neighboring
facets are <em>convex</em> if each centrum is below the
neighbor facet's hyperplane. </li>
<li>A <em>ridge</em> is a set of <i>d-1</i> vertices, two
neighboring facets, and an orientation. For example in
3-d, a ridge is a line segment. </li>
<li>A <em>non-simplicial facet</em> is a set of ridges, a
hyperplane equation, a centrum, an outer plane, and an
inner plane. The ridges determine a set of neighboring
facets, a set of vertices, and an orientation. Qhull
produces a non-simplicial facet when it merges two facets
together. For example, a cube has six non-simplicial
facets. </li>
</ul>
<p>For examples, use option '<a href="qh-opto.htm#f">f</a>'. See <a
href="../src/libqhull/qh-poly.htm">polyhedron operations</a> for further
design documentation. </p>
</blockquote>
<h3><a href="#TOC">&#187;</a>Imprecision in Qhull</h3>
<blockquote>
<p>See <a href="qh-impre.htm">Imprecision in Qhull</a> and <a href="qh-impre.htm#joggle">Merged facets or joggled input</a></p>
</blockquote>
<h3><a href="#TOC">&#187;</a>Examples of Qhull</h3>
<blockquote>
<p>See <a href="qh-eg.htm">Examples of Qhull</a>. Most of these examples require <a href="#geomview">Geomview</a>.
Some of the examples have <a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/welcome.html">pictures
</a>.</p>
</blockquote>
</blockquote>
<h2><a href="#TOC">&#187;</a>Options for using Qhull </h2>
<blockquote>
<p>See <a href="qh-quick.htm#options">Options</a>.</p>
</blockquote>
<h2><a href="#TOC">&#187;</a>Qhull internals </h2>
<blockquote>
<p>See <a href="qh-code.htm">Internals</a>.</p>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="geomview">Geomview, Qhull's
graphical viewer</a></h2>
<blockquote>
<p><a href="http://www.geomview.org">Geomview</a>
is an interactive geometry viewing program.
Geomview provides a good visualization of Qhull's 2-d and 3-d results.
<p>Qhull includes <a href="qh-eg.htm">Examples of Qhull</a> that may be viewed with Geomview.
<p>Geomview can help visulalize a 3-d Delaunay triangulation or the surface of a 4-d convex hull,
Use option '<a href="qh-optq.htm#QVn">QVn</a>' to select the 3-D facets adjacent to a vertex.
<p>You may use Geomview to create movies that animate your objects (c.f., <a href="http://www.geomview.org/FAQ/answers.shtml#mpeg">How can I create a video animation?</a>).
Geomview helped create the <a href="http://www.geom.uiuc.edu/video/">mathematical videos</a> "Not Knot", "Outside In", and "The Shape of Space" from the Geometry Center.
<h3><a href="#TOC">&#187;</a><a name="geomview-install">Installing Geomview</a></h3>
<blockquote>
<p>Geomview is an <a href=http://sourceforge.net/projects/geomview>open source project</a>
under SourceForge.
<p>
For build instructions see
<a href="http://www.geomview.org/download/">Downloading Geomview</a>.
Geomview builds under Linux, Unix, Macintosh OS X, and Windows.
<p>Geomview has <a href="https://packages.debian.org/search?keywords=geomview">installable packages</a> for Debian and Ubuntu.
The OS X build needs Xcode, an X11 SDK, and Lesstif or Motif.
The Windows build uses Cygwin (see <a href="#geomview-win">Building Geomview</a> below for instructions).
<p>If using Xforms (e.g., for Geomview's <a href="http://www.geomview.org/docs/html/Modules.html">External Modules</a>), install the 'libXpm-devel' package from cygwin and move the xforms directory into your geomview directory, e.g.,<br><tt>mv xforms-1.2.4 geomview-1.9.5/xforms</tt>
<p>Geomview's <a href="http://www.geom.uiuc.edu/software/geomview/docs/NDview/manpagehelp.html">ndview<a/> provides multiple views into 4-d and higher objects.
This module is out-of-date (<a href="http://sourceforge.net/p/geomview/mailman/message/2004152/">geomview-users: 4dview</a>).
Download NDview-sgi.tar.Z at <a href="ftp://www.geom.uiuc.edu/pub/software/geomview/newpieces/sgi">newpieces</a> and 4dview at <a href="https://stuff.mit.edu/afs/sipb/project/3d/arch/sgi_62/lib/Geomview/modules/">Geomview/modules</a>.
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="geomview-use">Using Geomview</a></h3>
<blockquote>
<p>Use Geomview to view <a href="qh-eg.htm">Examples of Qhull</a>. You can spin the convex hull, fly a camera through its facets,
and see how Qhull produces thick facets in response to round-off error.
<p>Follow these instructions to view 'eg,01.cube' from Examples of Qhull
<ol>
<li>Launch an XTerm command shell
<ul>
<li>If needed, start the X terminal server, Use 'xinit' or 'startx' in /usr/X11R6/bin<br><tt>xinit -- -multiwindow -clipboard</tt><br><tt>startx</tt>
<li>Start an XTerm command shell. In Windows, click the Cygwin/bash icon on your desktop.
<li>Set the DISPLAY variable, e.g.,<br><tt>export DISPLAY=:0</tt><br><tt>export DISPLAY=:0 >>~/.bashenv</tt>
</ul>
<li>Use Qhull's <a href="qh-optg.htm">Geomview options</a> to create a geomview object
<ul>
<li><tt>rbox c D3 | qconvex G >eg.01.cube</tt>
<li>On windows, convert the output to Unix text format with 'd2u'<br><tt>rbox c D3 | qconvex G | d2u >eg.01.cube</tt><br><tt>d2u eg.*</tt>
</ul>
<li>Run Geomview
<ul>
<li>Start Geomview with your example<br><tt>./geomview eg.01.cube</tt>
<li>Follow the instructions in <a href="http://www.geomview.org/docs/html/Tutorial.html">Gemoview Tutorial</a>
<li>Geomview creates the <i>Geomview control panel</i> with Targets and External Module, the <i>Geomview toolbar</i> with buttons for controlling Geomview, and the <i>Geomview camera window</i> showing a cube.
<li>Clear the camera window by selecting your object in the Targets list and 'Edit > Delete' or 'dd'
<li>Load the <i>Geomview files panel</i>. Select 'Open' in the 'File' menu.
<li>Set 'Filter' in the files panel to your example directory followed by '/*' (e.g., '/usr/local/qhull-2015.2/eg/*')
<li>Click 'Filter' in the files panel to view your examples in the 'Files' list.
<li>Load another example into the camera window by selecting it and clicking 'OK'.
<li>Review the instructions for <a href="http://www.geomview.org/docs/html/Interaction.html">Interacting with Geomview</a>
<li>When viewing multiple objects at once, you may want to turn off normalization. In the 'Inspect > Apperance' control panel, set 'Normalize' to 'None'.
</ul>
</ol>
<p>Geomview defines GCL (a textual API for controlling Geomview) and OOGL (a textual file format for defining objects).
<ul>
<li>To control Geomview, you may use any program that reads and writes from stdin and stdout. For example, it could report Qhull's information about a vertex identified by a double-click 'pick' event.
<li><a href="http://www.geomview.org/docs/html/GCL.html">GCL</a> command language for controlling Geomview
<li><a href="http://www.geomview.org/docs/html/OOGL-File-Formats.html">OOGL</a> file format for defining objects (<a href="http://www.geomview.org/docs/oogltour.html">tutorial</a>).
<li><a href="http://www.geomview.org/docs/html/Modules.html">External Modules</a> for interacting with Geomview via GCL
<li>Interact with your objects via <a href="http://www.geomview.org/docs/html/pick.html">pick</a> commands in response to right-mouse double clicks. Enable pick events with the <a href="http://www.geomview.org/docs/html/interest.html">interest</a> command.
</ul>
</blockquote>
<h3><a href="#TOC">&#187;</a><a name="geomview-win">Building Geomview for Windows</a></h3>
<blockquote>
<p>Compile Geomview under Cygwin. For detailed instructions, see
<a href="http://www.ee.surrey.ac.uk/Personal/L.Wood/software/SaVi/building-under-Windows/"
>Building Savi and Geomview under Windows</a>. These instructions are somewhat out-of-date. Updated
instructions follow.
<p>How to compile Geomview under 32-bit Cygwin (October 2015)</p>
<ol>
<li><b>Note:</b> L. Wood has run into multiple issues with Geomview on Cygwin. He recommends Virtualbox/Ubuntu
and a one-click install of geomview via the Ubuntu package. See his Savi/Geomview link above.
<li>Install 32-bit <a href="http://cygwin.com/">Cygwin</a> as follows.
For additional guidance, see Cygwin's <a href="https://cygwin.com/install.html">Installing and Updating Cygwin Packages</a>
and <a href="http://www.qhull.org/road/road-faq/xml/cmdline.xml#setup-cygwin">Setup cygwin</a>.
<ul>
<li>Launch the cygwin installer.
<li>Select a mirror from <a href="http://cygwin.com/mirrors.html">Cygwin mirrors</a> (e.g., http://mirrors.kernel.org/sourceware/cygwin/ in California).
<li>Select the packages to install. Besides the cygwin packages listed in the Savi/Windows instructions consider adding
<ul>
<li><b>Default</b> -- libXm-devel (required for /usr/include/Xm/Xm.h)
<li><b>Devel</b> -- bashdb, gcc-core (in place of gcc), gdb
<li><b>Lib</b> -- libGL-devel, libGLU1 (required, obsolete), libGLU-devel (required, obsolete), libjpeg-devel(XForms), libXext-devel (required), libXpm-devel (Xforms)
libGL and lib
<li><b>Math</b> -- bc
<li><b>Net</b> -- autossh, inetutils, openssh
<li><b>System</b> -- chere
<li><b>Utils</b> -- dos2unix (required for qhull), keychain
<li>If installing perl, ActiveState Perl may be a better choice than cygwin's perl. Perl is not used by Geomview or Qhull.
<li><a href="https://cygwin.com/cgi-bin2/package-grep.cgi">Cygwin Package Search</a> -- Search for cygwin programs and packages
</ul>
<li>Click 'Next' to download and install the packages.
<li>If the download is incomplete, try again.
<li>If you try again after a successful install, cygwin will uninstall and reinstall all modules..
<li>Click on the 'Cywin Terminal' icon on the Desktop. It sets up a user directory in /home from /etc/skel/...
<li>Mount your disk drives<br>mount c: /c # Ignore the warning /c does not exist
</ul>
<li>Consider installing the <a href="http://www.qhull.org/bash/doc/road-bash.html">Road Bash</a> scripts (/etc/road-*) from <a href="http://www.qhull.org/road/">Road</a>.
They define aliases and functions for Unix command shells (Unix, Linux, Mac OS X, Windows),
<ul>
<li>Download Road Bash and unzip the downloaded file
<li>Copy .../bash/etc/road-* to the Cywin /etc directory (by default, C:\cygwin\etc).
<li>Using the cygwin terminal, convert the road scripts to Unix format<br>d2u /etc/road-*
<li>Try it<br>source /etc/road-home.bashrc
<li>Install it<br>cp /etc/road-home.bashrc ~/.bashrc
</ul>
<li>Launch the X terminal server from '<tt>Start > All programs > Cygwin-X > Xwin Server</tt>'. Alternatively, run 'startx'
<li>Launch an XTerm shell
<ul>
<li>Right click the Cywin icon on the system tray in the Windows taskbar.
<li>Select '<tt>System Tools > XTerm</tt>'
</ul>
<li>Download and extract Geomview -- <a href="http://www.geomview.org/download/">Downloading Geomview</a>
<li>Compile Geomview
<ul>
<li>./configure
<li>make
</ul>
<li>If './configure' fails, check 'config.log' at the failing step. Look carefully for missing libraries, etc. The <a href="http://www.geomview.org/FAQ/answers.shtml">Geomview FAQ</a> contains suggestions (e.g., "configure claims it can't find OpenGl").
<li>If 'make' fails, read the output carefully for error messages. Usually it is a missing include file or package. Locate and install the missing cygwin packages
(<a href="https://cygwin.com/cgi-bin2/package-grep.cgi">Cygwin Package Search</a>).
</ol>
</blockquote>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="bugs">What to do if something
goes wrong</a></h2>
<blockquote>
<p>Please report bugs to <a href=mailto:qhull_bug@qhull.org>qhull_bug@qhull.org</a>
</a>. Please report if Qhull crashes. Please report if Qhull
generates an &quot;internal error&quot;. Please report if Qhull
produces a poor approximate hull in 2-d, 3-d or 4-d. Please
report documentation errors. Please report missing or incorrect
links.</p>
<p>If you do not understand something, try a small example. The <a
href="rbox.htm">rbox</a> program is an easy way to generate
test cases. The <a href="#geomview">Geomview</a> program helps to
visualize the output from Qhull.</p>
<p>If Qhull does not compile, it is due to an incompatibility
between your system and ours. The first thing to check is that
your compiler is ANSI standard. Qhull produces a compiler error
if __STDC__ is not defined. You may need to set a flag (e.g.,
'-A' or '-ansi').</p>
<p>If Qhull compiles but crashes on the test case (rbox D4),
there's still incompatibility between your system and ours.
Sometimes it is due to memory management. This can be turned off
with qh_NOmem in mem.h. Please let us know if you figure out how
to fix these problems. </p>
<p>If you doubt the output from Qhull, add option '<a
href="qh-optt.htm#Tv">Tv</a>'. It checks that every point is
inside the outer planes of the convex hull. It checks that every
facet is convex with its neighbors. It checks the topology of the
convex hull.</p>
<p>Qhull should work on all inputs. It may report precision
errors if you turn off merged facets with option '<a
href="qh-optq.htm#Q0">Q0</a>'. This can get as bad as facets with
flipped orientation or two facets with the same vertices. You'll
get a long help message if you run into such a case. They are
easy to generate with <tt>rbox</tt>.</p>
<p>If you do find a problem, try to simplify it before reporting
the error. Try different size inputs to locate the smallest one
that causes an error. You're welcome to hunt through the code
using the execution trace ('<a href="qh-optt.htm#Tn">T4</a>') as
a guide. This is especially true if you're incorporating Qhull
into your own program. </p>
<p>When you report an error, please attach a data set to the end
of your message. Include the options that you used with Qhull,
the results of option '<a href="qh-optf.htm#FO">FO</a>', and any
messages generated by Qhull. This allows me to see the error for
myself. Qhull is maintained part-time. </p>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="email">Email</a></h2>
<blockquote>
<p>Please send correspondence to Brad Barber at <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
and report bugs to <a href=mailto:qhull_bug@qhull.org>qhull_bug@qhull.org</a>
</a>. Let me know how you use Qhull. If you mention it in a
paper, please send a reference and abstract.</p>
<p>If you would like to get Qhull announcements (e.g., a new
version) and news (any bugs that get fixed, etc.), let us know
and we will add you to our mailing list. For Internet news about geometric algorithms
and convex hulls, look at comp.graphics.algorithms and
sci.math.num-analysis. For Qhull news look at <a
href="http://www.qhull.org/news">qhull-news.html</a>.</p>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="authors">Authors</a></h2>
<blockquote>
<pre>
C. Bradford Barber Hannu Huhdanpaa
bradb@shore.net hannu@qhull.org
</pre>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="acknowledge">Acknowledgments</a></h2>
<blockquote>
<p>A special thanks to David Dobkin for his guidance. A special
thanks to Albert Marden, Victor Milenkovic, the Geometry Center,
and Harvard University for supporting this work.</p>
<p>A special thanks to Mark Phillips, Robert Miner, and Stuart Levy for running the Geometry
Center web site long after the Geometry Center closed.
Stuart moved the web site to the University of Illinois at Champaign-Urbana.
Mark and Robert are founders of <a href=http://www.geomtech.com>Geometry Technologies</a>.
Mark, Stuart, and Tamara Munzner are the original authors of <a href=http://www.geomview.org>Geomview</a>.
<p>A special thanks to <a href="http://www.endocardial.com/">Endocardial
Solutions, Inc.</a> of St. Paul, Minnesota for their support of the
internal documentation (<a href=../src/libqhull/index.htm>src/libqhull/index.htm</a>). They use Qhull to build 3-d models of
heart chambers.</p>
<p>Qhull 1.0 and 2.0 were developed under National Science Foundation
grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. If you find
it useful, please let us know.</p>
<p>The Geometry Center was supported by grant DMS-8920161 from the
National Science Foundation, by grant DOE/DE-FG02-92ER25137 from
the Department of Energy, by the University of Minnesota, and by
Minnesota Technology, Inc.</p>
</blockquote>
<h2><a href="#TOC">&#187;</a><a name="ref">References</a></h2>
<blockquote>
<p><a name="aure91">Aurenhammer</a>, F., &quot;Voronoi diagrams
-- A survey of a fundamental geometric data structure,&quot; <i>ACM
Computing Surveys</i>, 1991, 23:345-405. </p>
<p><a name="bar-dob96">Barber</a>, C. B., D.P. Dobkin, and H.T.
Huhdanpaa, &quot;The Quickhull Algorithm for Convex Hulls,&quot; <i>ACM
Transactions on Mathematical Software</i>, 22(4):469-483, Dec 1996, www.qhull.org
[<a
href="http://portal.acm.org/citation.cfm?doid=235815.235821">http://portal.acm.org</a>;
<a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405">http://citeseerx.ist.psu.edu</a>].
</p>
<p><a name="cla-sho89">Clarkson</a>, K.L. and P.W. Shor,
&quot;Applications of random sampling in computational geometry,
II&quot;, <i>Discrete Computational Geometry</i>, 4:387-421, 1989</p>
<p><a name="cla-meh93">Clarkson</a>, K.L., K. Mehlhorn, and R.
Seidel, &quot;Four results on randomized incremental
construction,&quot; <em>Computational Geometry: Theory and
Applications</em>, vol. 3, p. 185-211, 1993.</p>
<p><a name="devi01">Devillers</a>, et. al.,
"Walking in a triangulation," <i>ACM Symposium on
Computational Geometry</i>, June 3-5,2001, Medford MA.
<p><a name="dob-kir90">Dobkin</a>, D.P. and D.G. Kirkpatrick,
&quot;Determining the separation of preprocessed polyhedra--a
unified approach,&quot; in <i>Proc. 17th Inter. Colloq. Automata
Lang. Program.</i>, in <i>Lecture Notes in Computer Science</i>,
Springer-Verlag, 443:400-413, 1990. </p>
<p><a name="edel01">Edelsbrunner</a>, H, <i>Geometry and Topology for Mesh Generation</i>,
Cambridge University Press, 2001.
<p><a name=gart99>Gartner, B.</a>, "Fast and robust smallest enclosing balls", <i>Algorithms - ESA '99</i>, LNCS 1643.
<p><a name=golub83>Golub, G.H. and van Loan, C.F.</a>, <i>Matric Computations</i>, Baltimore, Maryland, USA: John Hopkins Press, 1983
<p><a name="fort93">Fortune, S.</a>, &quot;Computational
geometry,&quot; in R. Martin, editor, <i>Directions in Geometric
Computation</i>, Information Geometers, 47 Stockers Avenue,
Winchester, SO22 5LB, UK, ISBN 1-874728-02-X, 1993.</p>
<p><a name="mile93">Milenkovic, V.</a>, &quot;Robust polygon
modeling,&quot; Computer-Aided Design, vol. 25, p. 546-566,
September 1993. </p>
<p><a name="muck96">Mucke</a>, E.P., I. Saias, B. Zhu, <i>Fast
randomized point location without preprocessing in Two- and
Three-dimensional Delaunay Triangulations</i>, ACM Symposium on
Computational Geometry, p. 274-283, 1996 [<a
href="http://www.geom.uiuc.edu/software/cglist/GeomDir/">GeomDir</a>].
</p>
<p><a name="mulm94">Mulmuley</a>, K., <i>Computational Geometry,
An Introduction Through Randomized Algorithms</i>, Prentice-Hall,
NJ, 1994.</p>
<p><a name="orou94">O'Rourke</a>, J., <i>Computational Geometry
in C</i>, Cambridge University Press, 1994.</p>
<p><a name="pre-sha85">Preparata</a>, F. and M. Shamos, <i>Computational
Geometry</i>, Springer-Verlag, New York, 1985.</p>
</blockquote>
<!-- Navigation links -->
<hr>
<p><b>Up:</b> <a
href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b><a
href="http://www.qhull.org/news">News</a> about Qhull<br>
<b>Up:</b> <a href="http://www.qhull.org/html/qh-faq.htm">FAQ</a> about Qhull<br>
<b>To:</b> <a href="#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>Dn:</b> <a href="qh-impre.htm">Imprecision in Qhull</a><br>
<b>Dn:</b> <a href="qh-eg.htm">Description of Qhull examples</a><br>
<b>Dn:</b> <a href="qh-code.htm">Qhull internals</a><br>
<b>Dn:</b> <a href="../src/libqhull/index.htm">Qhull functions, macros, and data
structures</a>
<!-- GC common information -->
<hr>
<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>
<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
</a><br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,630 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>qconvex -- convex hull</title>
</head>
<body>
<!-- Navigation links -->
<a name="TOP"><b>Up</b></a><b>:</b>
<a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> -- Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
src="qh--cone.gif" alt="[cone]" align="middle" width="100"
height="100"></a>qconvex -- convex hull</h1>
<p>The convex hull of a set of points is the smallest convex set
containing the points. See the detailed introduction by O'Rourke
[<a href="index.htm#orou94">'94</a>]. See <a
href="index.htm#description">Description of Qhull</a> and <a
href="qh-eg.htm#how">How Qhull adds a point</a>.</p>
<blockquote>
<dl>
<dt><b>Example:</b> rbox 10 D3 | qconvex <a
href="qh-opto.htm#s">s</a> <a href="qh-opto.htm#o">o</a> <a
href="qh-optt.htm#TO">TO result</a></dt>
<dd>Compute the 3-d convex hull of 10 random points. Write a
summary to the console and the points and facets to
'result'.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox c | qconvex <a
href="qh-opto.htm#n">n</a></dt>
<dd>Print the normals for each facet of a cube.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox c | qconvex <a
href="qh-opto.htm#i">i</a> <a href="qh-optq.htm#Qt">Qt</a></dt>
<dd>Print the triangulated facets of a cube.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox y 500 W0 | qconvex</dt>
<dd>Compute the convex hull of a simplex with 500
points on its surface.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox x W1e-12 1000 | qconvex
<a href="qh-optq.htm#QR">QR0</a></dt>
<dd>Compute the convex hull of 1000 points near the
surface of a randomly rotated simplex. Report
the maximum thickness of a facet.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox 1000 s | qconvex <a
href="qh-opto.htm#s">s</a> <a
href="qh-optf.htm#FA">FA</a> </dt>
<dd>Compute the convex hull of 1000 cospherical
points. Verify the results and print a summary
with the total area and volume.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox d D12 | qconvex <a
href="qh-optq.htm#QRn">QR0</a> <a
href="qh-optf.htm#FA">FA</a></dt>
<dd>Compute the convex hull of a 12-d diamond.
Randomly rotate the input. Note the large number
of facets and the small volume.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox c D7 | qconvex <a
href="qh-optf.htm#FA">FA</a> <a
href="qh-optt.htm#TFn">TF1000</a></dt>
<dd>Compute the convex hull of the 7-d hypercube.
Report on progress every 1000 facets. Computing
the convex hull of the 9-d hypercube takes too
much time and space. </dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox c d D2 | qconvex <a
href="qh-optq.htm#Qc">Qc</a> <a
href="qh-opto.htm#s">s</a> <a
href="qh-opto.htm#f">f</a> <a
href="qh-optf.htm#Fx">Fx</a> | more</dt>
<dd>Dump all fields of all facets for a square and a
diamond. Also print a summary and a list of
vertices. Note the coplanar points.</dd>
<dt>&nbsp;</dt>
</dl>
</blockquote>
<p>Except for rbox, all of the qhull programs compute a convex hull.
<p>By default, Qhull merges coplanar facets. For example, the convex
hull of a cube's vertices has six facets.
<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
all facets will be simplicial (e.g., triangles in 2-d). For the cube
example, it will have 12 facets. Some facets may be
degenerate and have zero area.
<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input),
all facets will be simplicial. The corresponding vertices will be
slightly perturbed and identical points will be joggled apart.
Joggled input is less accurate that triangulated
output.See <a
href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
<p>The output for 4-d convex hulls may be confusing if the convex
hull contains non-simplicial facets (e.g., a hypercube). See
<a href=qh-faq.htm#extra>Why
are there extra points in a 4-d or higher convex hull?</a><br>
</p>
</p>
<p>The 'qconvex' program is equivalent to
'<a href=qhull.htm#outputs>qhull</a>' in 2-d to 4-d, and
'<a href=qhull.htm#outputs>qhull</a> <a href=qh-optq.htm#Qx>Qx</a>'
in 5-d and higher. It disables the following Qhull
<a href=qh-quick.htm#options>options</a>: <i>d v H Qbb Qf Qg Qm
Qr Qu Qv Qx Qz TR E V Fp Gt Q0,etc</i>.
<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h3><a href="#TOP">&#187;</a><a name="synopsis">qconvex synopsis</a></h3>
<pre>
qconvex- compute the convex hull.
input (stdin): dimension, number of points, point coordinates
comments start with a non-numeric character
options (qconvex.htm):
Qt - triangulated output
QJ - joggle input instead of merging facets
Tv - verify result: structure, convexity, and point inclusion
. - concise list of all options
- - one-line description of all options
output options (subset):
s - summary of results (default)
i - vertices incident to each facet
n - normals with offsets
p - vertex coordinates (includes coplanar points if 'Qc')
Fx - extreme points (convex hull vertices)
FA - compute total area and volume
o - OFF format (dim, n, points, facets)
G - Geomview output (2-d, 3-d, and 4-d)
m - Mathematica output (2-d and 3-d)
QVn - print facets that include point n, -n if not
TO file- output results to file, may be enclosed in single quotes
examples:
rbox c D2 | qconvex s n rbox c D2 | qconvex i
rbox c D2 | qconvex o rbox 1000 s | qconvex s Tv FA
rbox c d D2 | qconvex s Qc Fx rbox y 1000 W0 | qconvex s n
rbox y 1000 W0 | qconvex s QJ rbox d G1 D12 | qconvex QR0 FA Pp
rbox c D7 | qconvex FA TF1000
</pre>
<h3><a href="#TOP">&#187;</a><a name="input">qconvex
input</a></h3>
<blockquote>
<p>The input data on <tt>stdin</tt> consists of:</p>
<ul>
<li>dimension
<li>number of points</li>
<li>point coordinates</li>
</ul>
<p>Use I/O redirection (e.g., qconvex &lt; data.txt), a pipe (e.g., rbox 10 | qconvex),
or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qconvex TI data.txt).
<p>Comments start with a non-numeric character. Error reporting is
simpler if there is one point per line. Dimension
and number of points may be reversed.
<p>Here is the input for computing the convex
hull of the unit cube. The output is the normals, one
per facet.</p>
<blockquote>
<p>rbox c &gt; data </p>
<pre>
3 RBOX c
8
-0.5 -0.5 -0.5
-0.5 -0.5 0.5
-0.5 0.5 -0.5
-0.5 0.5 0.5
0.5 -0.5 -0.5
0.5 -0.5 0.5
0.5 0.5 -0.5
0.5 0.5 0.5
</pre>
<p>qconvex s n &lt; data</p>
<pre>
Convex hull of 8 points in 3-d:
Number of vertices: 8
Number of facets: 6
Number of non-simplicial facets: 6
Statistics for: RBOX c | QCONVEX s n
Number of points processed: 8
Number of hyperplanes created: 11
Number of distance tests for qhull: 35
Number of merged facets: 6
Number of distance tests for merging: 84
CPU seconds to compute hull (after input): 0.081
4
6
0 0 -1 -0.5
0 -1 0 -0.5
1 0 0 -0.5
-1 0 0 -0.5
0 1 0 -0.5
0 0 1 -0.5
</pre>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="outputs">qconvex outputs</a></h3>
<blockquote>
<p>These options control the output of qconvex. They may be used
individually or together.</p>
<blockquote>
<dl compact>
<dt>&nbsp;</dt>
<dd><b>Vertices</b></dd>
<dt><a href="qh-optf.htm#Fx">Fx</a></dt>
<dd>list extreme points (i.e., vertices). The first line is the number of
extreme points. Each point is listed, one per line. The cube example
has eight vertices.</dd>
<dt><a href="qh-optf.htm#Fv">Fv</a></dt>
<dd>list vertices for each facet. The first line is the number of facets.
Each remaining line starts with the number of vertices. For the cube example,
each facet has four vertices.</dd>
<dt><a href="qh-opto.htm#i">i</a></dt>
<dd>list vertices for each facet. The first line is the number of facets. The
remaining lines list the vertices for each facet. In 4-d and
higher, triangulate non-simplicial facets by adding an extra point.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Coordinates</b></dd>
<dt><a href="qh-opto.htm#o">o</a></dt>
<dd>print vertices and facets of the convex hull in OFF format. The
first line is the dimension. The second line is the number of
vertices, facets, and ridges. The vertex
coordinates are next, followed by the facets. Each facet starts with
the number of vertices. The cube example has four vertices per facet.</dd>
<dt><a href="qh-optf.htm#Ft">Ft</a></dt>
<dd>print a triangulation of the convex hull in OFF format. The first line
is the dimension. The second line is the number of vertices and added points,
followed by the number of facets and the number of ridges.
The vertex coordinates are next, followed by the centrum coordinates. There is
one centrum for each non-simplicial facet.
The cube example has six centrums, one per square.
Each facet starts with the number of vertices or centrums.
In the cube example, each facet uses two vertices and one centrum.</dd>
<dt><a href="qh-opto.htm#p">p</a></dt>
<dd>print vertex coordinates. The first line is the dimension and the second
line is the number of vertices. The following lines are the coordinates of each
vertex. The cube example has eight vertices.</dd>
<dt><a href="qh-optq.htm#Qc">Qc</a> <a href="qh-opto.htm#p">p</a></dt>
<dd>print coordinates of vertices and coplanar points. The first line is the dimension.
The second line is the number of vertices and coplanar points. The coordinates
are next, one line per point. Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a> p'
to print the coordinates of all points.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Facets</b></dd>
<dt><a href="qh-optf.htm#Fn">Fn</a></dt>
<dd>list neighboring facets for each facet. The first line is the
number of facets. Each remaining line starts with the number of
neighboring facets. The cube example has four neighbors per facet.</dd>
<dt><a href="qh-optf.htm#FN">FN</a></dt>
<dd>list neighboring facets for each point. The first line is the
total number of points. Each remaining line starts with the number of
neighboring facets. Each vertex of the cube example has three neighboring
facets. Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a> FN'
to include coplanar and interior points. </dd>
<dt><a href="qh-optf.htm#Fa">Fa</a></dt>
<dd>print area for each facet. The first line is the number of facets.
Facet area follows, one line per facet. For the cube example, each facet has area one.</dd>
<dt><a href="qh-optf.htm#FI">FI</a></dt>
<dd>list facet IDs. The first line is the number of
facets. The IDs follow, one per line.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Coplanar and interior points</b></dd>
<dt><a href="qh-optf.htm#Fc">Fc</a></dt>
<dd>list coplanar points for each facet. The first line is the number
of facets. The remaining lines start with the number of coplanar points.
A coplanar point is assigned to one facet.</dd>
<dt><a href="qh-optq.htm#Qi">Qi</a> <a href="qh-optf.htm#Fc">Fc</a></dt>
<dd>list interior points for each facet. The first line is the number
of facets. The remaining lines start with the number of interior points.
A coplanar point is assigned to one facet.</dd>
<dt><a href="qh-optf.htm#FP">FP</a></dt>
<dd>print distance to nearest vertex for coplanar points. The first line is the
number of coplanar points. Each remaining line starts with the point ID of
a vertex, followed by the point ID of a coplanar point, its facet, and distance.
Use '<a href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a>
<a href="qh-optf.htm#FP">FP</a>' for coplanar and interior points.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Hyperplanes</b></dd>
<dt><a href="qh-opto.htm#n">n</a></dt>
<dd>print hyperplane for each facet. The first line is the dimension. The
second line is the number of facets. Each remaining line is the hyperplane's
coefficients followed by its offset.</dd>
<dt><a href="qh-optf.htm#Fo">Fo</a></dt>
<dd>print outer plane for each facet. The output plane is above all points.
The first line is the dimension. The
second line is the number of facets. Each remaining line is the outer plane's
coefficients followed by its offset.</dd>
<dt><a href="qh-optf.htm#Fi">Fi</a></dt>
<dd>print inner plane for each facet. The inner plane of a facet is
below its vertices.
The first line is the dimension. The
second line is the number of facets. Each remaining line is the inner plane's
coefficients followed by its offset.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>General</b></dd>
<dt><a href="qh-opto.htm#s">s</a></dt>
<dd>print summary for the convex hull. Use '<a
href="qh-optf.htm#Fs">Fs</a>' and '<a
href="qh-optf.htm#FS">FS</a>' if you need numeric data.</dd>
<dt><a href="qh-optf.htm#FA">FA</a></dt>
<dd>compute total area and volume for '<a
href="qh-opto.htm#s">s</a>' and '<a href="qh-optf.htm#FS">FS</a>'</dd>
<dt><a href="qh-opto.htm#m">m</a></dt>
<dd>Mathematica output for the convex hull in 2-d or 3-d.</dd>
<dt><a href="qh-optf.htm#FM">FM</a></dt>
<dd>Maple output for the convex hull in 2-d or 3-d.</dd>
<dt><a href="qh-optg.htm#G">G</a></dt>
<dd>Geomview output for the convex hull in 2-d, 3-d, or 4-d.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Scaling and rotation</b></dd>
<dt><a href="qh-optq.htm#Qbk">Qbk:n</a></dt>
<dd>scale k'th coordinate to lower bound.</dd>
<dt><a href="qh-optq.htm#QBk">QBk:n</a></dt>
<dd>scale k'th coordinate to upper bound.</dd>
<dt><a href="qh-optq.htm#QbB">QbB</a></dt>
<dd>scale input to unit cube centered at the origin.</dd>
<dt><a href="qh-optq.htm#QRn">QRn</a></dt>
<dd>randomly rotate the input with a random seed of n. If n=0, the
seed is the time. If n=-1, use time for the random seed, but do
not rotate the input.</dd>
<dt><a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a></dt>
<dd>remove k'th coordinate from input. This computes the
convex hull in one lower dimension.</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="controls">qconvex controls</a></h3>
<blockquote>
<p>These options provide additional control:</p>
<blockquote>
<dl compact>
<dt><a href="qh-optq.htm#Qt">Qt</a></dt>
<dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
degenerate facets of zero area.</dd>
<dt><a href="qh-optq.htm#QJn">QJ</a></dt>
<dd>joggle the input instead of merging facets. This guarantees simplicial facets
(e.g., triangles in 3-d). It is less accurate than triangulated output ('Qt').</dd>
<dt><a href="qh-optq.htm#Qc">Qc</a></dt>
<dd>keep coplanar points</dd>
<dt><a href="qh-optq.htm#Qi">Qi</a></dt>
<dd>keep interior points</dd>
<dt><a href="qh-opto.htm#f">f </a></dt>
<dd>facet dump. Print the data structure for each facet.</dd>
<dt><a href="qh-optq.htm#QVn">QVn</a></dt>
<dd>select facets containing point <em>n</em> as a vertex,</dd>
<dt><a href="qh-optq.htm#QGn">QGn</a></dt>
<dd>select facets that are visible from point <em>n</em>
(marked 'good'). Use <em>-n</em> for the remainder.</dd>
<dt><a href="qh-optp.htm#PDk">PDk:0</a></dt>
<dd>select facets with a negative coordinate for dimension <i>k</i></dd>
<dt><a href="qh-optt.htm#TFn">TFn</a></dt>
<dd>report progress after constructing <em>n</em> facets</dd>
<dt><a href="qh-optt.htm#Tv">Tv</a></dt>
<dd>verify result</dd>
<dt><a href="qh-optt.htm#TO">TI file</a></dt>
<dd>input data from file. The filename may not use spaces or quotes.</dd>
<dt><a href="qh-optt.htm#TO">TO file</a></dt>
<dd>output results to file. Use single quotes if the filename
contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
<dt><a href="qh-optq.htm#Qs">Qs</a></dt>
<dd>search all points for the initial simplex. If Qhull can
not construct an initial simplex, it reports a
descriptive message. Usually, the point set is degenerate and one
or more dimensions should be removed ('<a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a>').
If not, use option 'Qs'. It performs an exhaustive search for the
best initial simplex. This is expensive is high dimensions.</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="graphics">qconvex graphics</a></h3>
<blockquote>
<p>Display 2-d, 3-d, and 4-d convex hulls with Geomview ('<a
href="qh-optg.htm#G">G</a>').</p>
<p>Display 2-d and 3-d convex hulls with Mathematica ('<a
href="qh-opto.htm#m">m</a>').</p>
<p>To view 4-d convex hulls in 3-d, use '<a
href="qh-optp.htm#Pdk">Pd0d1d2d3</a>' to select the positive
octant and '<a href="qh-optg.htm#GDn">GrD2</a>' to drop dimension
2. </p>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="notes">qconvex notes</a></h3>
<blockquote>
<p>Qhull always computes a convex hull. The
convex hull may be used for other geometric structures. The
general technique is to transform the structure into an
equivalent convex hull problem. For example, the Delaunay
triangulation is equivalent to the convex hull of the input sites
after lifting the points to a paraboloid.</p>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="conventions">qconvex
conventions</a></h3>
<blockquote>
<p>The following terminology is used for convex hulls in Qhull.
See <a href="index.htm#structure">Qhull's data structures</a>.</p>
<ul>
<li><em>point</em> - <em>d</em> coordinates</li>
<li><em>vertex</em> - extreme point of the input set</li>
<li><em>ridge</em> - <i>d-1</i> vertices between two
neighboring facets</li>
<li><em>hyperplane</em> - halfspace defined by a unit normal
and offset</li>
<li><em>coplanar point</em> - a nearly incident point to a
hyperplane</li>
<li><em>centrum</em> - a point on the hyperplane for testing
convexity</li>
<li><em>facet</em> - a facet with vertices, ridges, coplanar
points, neighboring facets, and hyperplane</li>
<li><em>simplicial facet</em> - a facet with <em>d</em>
vertices, <em>d</em> ridges, and <em>d</em> neighbors</li>
<li><em>non-simplicial facet</em> - a facet with more than <em>d</em>
vertices</li>
<li><em>good facet</em> - a facet selected by '<a
href="qh-optq.htm#QVn">QVn</a>', etc.</li>
</ul>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="options">qconvex options</a></h3>
<pre>
qconvex- compute the convex hull
http://www.qhull.org
input (stdin):
first lines: dimension and number of points (or vice-versa).
other lines: point coordinates, best if one point per line
comments: start with a non-numeric character
options:
Qt - triangulated output
QJ - joggle input instead of merging facets
Qc - keep coplanar points with nearest facet
Qi - keep interior points with nearest facet
Qhull control options:
Qbk:n - scale coord k so that low bound is n
QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
QbB - scale input to unit cube centered at the origin
Qbk:0Bk:0 - remove k-th coordinate from input
QJn - randomly joggle input in range [-n,n]
QRn - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
Qs - search all points for the initial simplex
QGn - good facet if visible from point n, -n for not visible
QVn - good facet if it includes point n, -n if not
Trace options:
T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
Tc - check frequently during execution
Ts - print statistics
Tv - verify result: structure, convexity, and point inclusion
Tz - send all output to stdout
TFn - report summary when n or more facets created
TI file - input data from file, no spaces or single quotes
TO file - output results to file, may be enclosed in single quotes
TPn - turn on tracing when point n added to hull
TMn - turn on tracing at merge n
TWn - trace merge facets when width > n
TVn - stop qhull after adding point n, -n for before (see TCn)
TCn - stop qhull after building cone for point n (see TVn)
Precision options:
Cn - radius of centrum (roundoff added). Merge facets if non-convex
An - cosine of maximum angle. Merge facets if cosine > n or non-convex
C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
Rn - randomly perturb computations by a factor of [1-n,1+n]
Un - max distance below plane for a new, coplanar point
Wn - min facet width for outside point (before roundoff)
Output formats (may be combined; if none, produces a summary to stdout):
f - facet dump
G - Geomview output (see below)
i - vertices incident to each facet
m - Mathematica output (2-d and 3-d)
n - normals with offsets
o - OFF file format (dim, points and facets; Voronoi regions)
p - point coordinates
s - summary (stderr)
More formats:
Fa - area for each facet
FA - compute total area and volume for option 's'
Fc - count plus coplanar points for each facet
use 'Qc' (default) for coplanar and 'Qi' for interior
FC - centrum for each facet
Fd - use cdd format for input (homogeneous with offset first)
FD - use cdd format for numeric output (offset first)
FF - facet dump without ridges
Fi - inner plane for each facet
FI - ID for each facet
Fm - merge count for each facet (511 max)
FM - Maple output (2-d and 3-d)
Fn - count plus neighboring facets for each facet
FN - count plus neighboring facets for each point
Fo - outer plane (or max_outside) for each facet
FO - options and precision constants
FP - nearest vertex for each coplanar point
FQ - command used for qconvex
Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
for output: #vertices, #facets,
#coplanar points, #non-simplicial facets
#real (2), max outer plane, min vertex
FS - sizes: #int (0)
#real(2) tot area, tot volume
Ft - triangulation with centrums for non-simplicial facets (OFF format)
Fv - count plus vertices for each facet
FV - average of vertices (a feasible point for 'H')
Fx - extreme points (in order for 2-d)
Geomview output (2-d, 3-d, and 4-d)
Ga - all points as dots
Gp - coplanar points and vertices as radii
Gv - vertices as spheres
Gi - inner planes only
Gn - no planes
Go - outer planes only
Gc - centrums
Gh - hyperplane intersections
Gr - ridges
GDn - drop dimension n in 3-d and 4-d output
Print options:
PAn - keep n largest facets by area
Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
PDk:n - drop facet if normal[k] >= n
Pg - print good facets (needs 'QGn' or 'QVn')
PFn - keep facets whose area is at least n
PG - print neighbors of good facets
PMn - keep n facets with most merges
Po - force output. If error, output neighborhood of facet
Pp - do not report precision problems
. - list of all options
- - one line descriptions of all options
</pre>
<!-- Navigation links -->
<hr>
<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149;<a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<!-- GC common information -->
<hr>
<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>
<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
</a><br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>

View file

@ -0,0 +1,416 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>qdelaunay Qu -- furthest-site Delaunay triangulation</title>
</head>
<body>
<!-- Navigation links -->
<a name="TOP"><b>Up</b></a><b>:</b>
<a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
height="100"></a>qdelaunay Qu -- furthest-site Delaunay triangulation</h1>
<p>The furthest-site Delaunay triangulation corresponds to the upper facets of the <a href="qdelaun.htm">Delaunay construction</a>.
Its vertices are the
extreme points of the input sites.
It is the dual of the <a
href="qvoron_f.htm">furthest-site Voronoi diagram</a>.
<blockquote>
<dl>
<dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
href="qh-optq.htm#Qu">Qu</a> <a
href="qh-optq.htm#Qt">Qt</a> <a href="qh-opto.htm#s">s</a>
<a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
result</a></dt>
<dd>Compute the 2-d, furthest-site Delaunay triangulation of 10 random
points. Triangulate the output.
Write a summary to the console and the regions to
'result'.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
href="qh-optq.htm#Qu">Qu</a> <a
href="qh-optq.htm#QJn">QJ</a> <a href="qh-opto.htm#s">s</a>
<a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
result</a></dt>
<dd>Compute the 2-d, furthest-site Delaunay triangulation of 10 random
points. Joggle the input to guarantee triangular output.
Write a summary to the console and the regions to
'result'.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox r y c G1 D2 | qdelaunay <a
href="qh-optq.htm#Qu">Qu</a> <a href="qh-opto.htm#s">s</a>
<a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optt.htm#TO">TO
result</a></dt>
<dd>Compute the 2-d, furthest-site Delaunay triangulation of a triangle inside
a square.
Write a summary to the console and unoriented regions to 'result'.
Merge regions for cocircular input sites (e.g., the square).
The square is the only furthest-site
Delaunay region.</dd>
</dl>
</blockquote>
<p>As with the Delaunay triangulation, Qhull computes the
furthest-site Delaunay triangulation by lifting the input sites to a
paraboloid. The lower facets correspond to the Delaunay
triangulation while the upper facets correspond to the
furthest-site triangulation. Neither triangulation includes
&quot;vertical&quot; facets (i.e., facets whose last hyperplane
coefficient is nearly zero). Vertical facets correspond to input
sites that are coplanar to the convex hull of the input. An
example is points on the boundary of a lattice.</p>
<p>By default, qdelaunay merges cocircular and cospherical regions.
For example, the furthest-site Delaunay triangulation of a square inside a diamond
('rbox D2 c d G4 | qdelaunay Qu') consists of one region (the diamond).
<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d).
Some regions may be
degenerate and have zero area.
<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input), all furthest-site
Delaunay regions
will be simplicial (e.g., triangles in 2-d). Joggled input
is less accurate than triangulated output ('Qt'). See <a
href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
<p>The output for 3-d, furthest-site Delaunay triangulations may be confusing if the
input contains cospherical data. See the FAQ item
<a href=qh-faq.htm#extra>Why
are there extra points in a 4-d or higher convex hull?</a>
Avoid these problems with triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or
joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
</p>
<p>The 'qdelaunay' program is equivalent to
'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
in 4-d and higher. It disables the following Qhull
<a href=qh-quick.htm#options>options</a>: <i>d n v H U Qb QB Qc Qf Qg Qi
Qm Qr QR Qv Qx TR E V FC Fi Fo Fp FV Q0,etc</i>.
<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h3><a href="#TOP">&#187;</a><a name="synopsis">furthest-site qdelaunay synopsis</a></h3>
<blockquote>
See <a href="qdelaun.htm#synopsis">qdelaunay synopsis</a>. The same
program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
for furthest-site Delaunay triangulations.
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="input">furthest-site qdelaunay
input</a></h3>
<blockquote>
<p>The input data on <tt>stdin</tt> consists of:</p>
<ul>
<li>dimension
<li>number of points</li>
<li>point coordinates</li>
</ul>
<p>Use I/O redirection (e.g., qdelaunay Qu &lt; data.txt), a pipe (e.g., rbox 10 | qdelaunay Qu),
or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qdelaunay Qu TI data.txt).
<p>For example, this is a square containing four random points.
Its furthest-site Delaunay
triangulation contains one square.
<p>
<blockquote>
<tt>rbox c 4 D2 &gt; data</tt>
<blockquote><pre>
2 RBOX c 4 D2
8
-0.4999921736307369 -0.3684622117955817
0.2556053225468894 -0.0413498678629751
0.0327672376602583 -0.2810408135699488
-0.452955383763607 0.17886471718444
-0.5 -0.5
-0.5 0.5
0.5 -0.5
0.5 0.5
</pre></blockquote>
<p><tt>qdelaunay Qu i &lt; data</tt>
<blockquote><pre>
Furthest-site Delaunay triangulation by the convex hull of 8 points in 3-d:
Number of input sites: 8
Number of Delaunay regions: 1
Number of non-simplicial Delaunay regions: 1
Statistics for: RBOX c 4 D2 | QDELAUNAY s Qu i
Number of points processed: 8
Number of hyperplanes created: 20
Number of facets in hull: 11
Number of distance tests for qhull: 34
Number of merged facets: 1
Number of distance tests for merging: 107
CPU seconds to compute hull (after input): 0.02
1
7 6 4 5
</pre></blockquote>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="outputs">furthest-site qdelaunay
outputs</a></h3>
<blockquote>
<p>These options control the output of furthest-site Delaunay triangulations:</p>
<blockquote>
<dl compact>
<dd><b>furthest-site Delaunay regions</b></dd>
<dt><a href="qh-opto.htm#i">i</a></dt>
<dd>list input sites for each furthest-site Delaunay region. The first line is the number of regions. The
remaining lines list the input sites for each region. The regions are
oriented. In 3-d and
higher, report cospherical sites by adding extra points. For the points-in-square example,
the square is the only furthest-site Delaunay region.</dd>
<dt><a href="qh-optf.htm#Fv">Fv</a></dt>
<dd>list input sites for each furthest-site Delaunay region. The first line is the number of regions.
Each remaining line starts with the number of input sites. The regions
are unoriented. For the points-in-square example,
the square is the only furthest-site Delaunay region.</dd>
<dt><a href="qh-optf.htm#Ft">Ft</a></dt>
<dd>print a triangulation of the furthest-site Delaunay regions in OFF format. The first line
is the dimension. The second line is the number of input sites and added points,
followed by the number of simplices and the number of ridges.
The input coordinates are next, followed by the centrum coordinates. There is
one centrum for each non-simplicial furthest-site Delaunay region. Each remaining line starts
with dimension+1. The
simplices are oriented.
For the points-in-square example, the square has a centrum at the
origin. It splits the square into four triangular regions.</dd>
<dt><a href="qh-optf.htm#Fn">Fn</a></dt>
<dd>list neighboring regions for each furthest-site Delaunay region. The first line is the
number of regions. Each remaining line starts with the number of
neighboring regions. Negative indices (e.g., <em>-1</em>) indicate regions
outside of the furthest-site Delaunay triangulation.
For the points-in-square example, the four neighboring regions
are outside of the triangulation. They belong to the regular
Delaunay triangulation.</dd>
<dt><a href="qh-optf.htm#FN">FN</a></dt>
<dd>list the furthest-site Delaunay regions for each input site. The first line is the
total number of input sites. Each remaining line starts with the number of
furthest-site Delaunay regions. Negative indices (e.g., <em>-1</em>) indicate regions
outside of the furthest-site Delaunay triangulation.
For the points-in-square example, the four random points belong to no region
while the square's vertices belong to region <em>0</em> and three
regions outside of the furthest-site Delaunay triangulation.</dd>
<dt><a href="qh-optf.htm#Fa">Fa</a></dt>
<dd>print area for each furthest-site Delaunay region. The first line is the number of regions.
The areas follow, one line per region. For the points-in-square example, the
square has unit area. </dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Input sites</b></dd>
<dt><a href="qh-optf.htm#Fx">Fx</a></dt>
<dd>list extreme points of the input sites. These points are vertices of the furthest-point
Delaunay triangulation. They are on the
boundary of the convex hull. The first line is the number of
extreme points. Each point is listed, one per line. The points-in-square example
has four extreme points.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>General</b></dd>
<dt><a href="qh-optf.htm#FA">FA</a></dt>
<dd>compute total area for '<a href="qh-opto.htm#s">s</a>'
and '<a href="qh-optf.htm#FS">FS</a>'. This is the
same as the area of the convex hull.</dd>
<dt><a href="qh-opto.htm#o">o</a></dt>
<dd>print upper facets of the corresponding convex hull (a
paraboloid)</dd>
<dt><a href="qh-opto.htm#m">m</a></dt>
<dd>Mathematica output for the upper facets of the paraboloid (2-d triangulations).</dd>
<dt><a href="qh-optf.htm#FM">FM</a></dt>
<dd>Maple output for the upper facets of the paraboloid (2-d triangulations).</dd>
<dt><a href="qh-optg.htm#G">G</a></dt>
<dd>Geomview output for the paraboloid (2-d or 3-d triangulations).</dd>
<dt><a href="qh-opto.htm#s">s</a></dt>
<dd>print summary for the furthest-site Delaunay triangulation. Use '<a
href="qh-optf.htm#Fs">Fs</a>' and '<a
href="qh-optf.htm#FS">FS</a>' for numeric data.</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="controls">furthest-site qdelaunay
controls</a></h3>
<blockquote>
<p>These options provide additional control:</p>
<blockquote>
<dl compact>
<dt><a href="qh-optq.htm#Qu">Qu</a></dt>
<dd>must be used for furthest-site Delaunay triangulation.</dd>
<dt><a href="qh-optq.htm#Qt">Qt</a></dt>
<dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
degenerate facets of zero area.</dd>
<dt><a href="qh-optq.htm#QJn">QJ</a></dt>
<dd>joggle the input to avoid cospherical and coincident
sites. It is less accurate than triangulated output ('Qt').</dd>
<dt><a href="qh-optq.htm#QVn">QVn</a></dt>
<dd>select facets adjacent to input site <em>n</em> (marked
'good').</dd>
<dt><a href="qh-optt.htm#Tv">Tv</a></dt>
<dd>verify result.</dd>
<dt><a href="qh-optt.htm#TO">TI file</a></dt>
<dd>input data from file. The filename may not use spaces or quotes.</dd>
<dt><a href="qh-optt.htm#TO">TO file</a></dt>
<dd>output results to file. Use single quotes if the filename
contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
<dt><a href="qh-optt.htm#TFn">TFn</a></dt>
<dd>report progress after constructing <em>n</em> facets</dd>
<dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
<dd>include upper and lower facets in the output. Set <em>k</em>
to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
<dt><a href="qh-opto.htm#f">f</a></dt>
<dd>facet dump. Print the data structure for each facet (i.e., furthest-site Delaunay region).</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="graphics">furthest-site qdelaunay
graphics</a></h3>
<blockquote>
See <a href="qdelaun.htm#graphics">Delaunay graphics</a>.
They are the same except for Mathematica and Maple output.
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="notes">furthest-site
qdelaunay notes</a></h3>
<blockquote>
<p>The furthest-site Delaunay triangulation does not
record coincident input sites. Use <tt>qdelaunay</tt> instead.
<p><tt>qdelaunay Qu</tt> does not work for purely cocircular
or cospherical points (e.g., rbox c | qdelaunay Qu). Instead,
use <tt>qdelaunay Qz</tt> -- when all points are vertices of the convex
hull of the input sites, the Delaunay triangulation is the same
as the furthest-site Delaunay triangulation.
<p>A non-simplicial, furthest-site Delaunay region indicates nearly cocircular or
cospherical input sites. To avoid non-simplicial regions triangulate
the output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggle
the input ('<a href="qh-optq.htm#QJn">QJ</a>'). Joggled input
is less accurate than triangulated output.
You may also triangulate
non-simplicial regions with option '<a
href="qh-optf.htm#Ft">Ft</a>'. It adds
the centrum to non-simplicial regions. Alternatively, use an <a
href="qh-impre.htm#exact">exact arithmetic code</a>.</p>
<p>Furthest-site Delaunay triangulations do not include facets that are
coplanar with the convex hull of the input sites. A facet is
coplanar if the last coefficient of its normal is
nearly zero (see <a href="../src/libqhull/user.h#ZEROdelaunay">qh_ZEROdelaunay</a>).
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="conventions">furthest-site qdelaunay conventions</a></h3>
<blockquote>
<p>The following terminology is used for furthest-site Delaunay
triangulations in Qhull. The underlying structure is the upper
facets of a convex hull in one higher dimension. See <a
href="qconvex.htm#conventions">convex hull conventions</a>, <a
href="qdelaun.htm#conventions">Delaunay conventions</a>,
and <a href="index.htm#structure">Qhull's data structures</a></p>
<blockquote>
<ul>
<li><em>input site</em> - a point in the input (one dimension
lower than a point on the convex hull)</li>
<li><em>point</em> - <i>d+1</i> coordinates. The last
coordinate is the sum of the squares of the input site's
coordinates</li>
<li><em>vertex</em> - a point on the paraboloid. It
corresponds to a unique input site. </li>
<li><em>furthest-site Delaunay facet</em> - an upper facet of the
paraboloid. The last coefficient of its normal is
clearly positive.</li>
<li><em>furthest-site Delaunay region</em> - a furthest-site Delaunay
facet projected to the input sites</li>
<li><em>non-simplicial facet</em> - more than <em>d</em>
points are cocircular or cospherical</li>
<li><em>good facet</em> - a furthest-site Delaunay facet with optional
restrictions by '<a href="qh-optq.htm#QVn">QVn</a>', etc.</li>
</ul>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="options">furthest-site qdelaunay options</a></h3>
<blockquote>
See <a href="qdelaun.htm#options">qdelaunay options</a>. The same
program is used for both constructions. Use option '<a href="qh-optq.htm#Qu">Qu</a>'
for furthest-site Delaunay triangulations.
</blockquote>
<!-- Navigation links -->
<hr>
<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<!-- GC common information -->
<hr>
<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>
<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
</a><br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>

View file

@ -0,0 +1,628 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>qdelaunay -- Delaunay triangulation</title>
</head>
<body>
<!-- Navigation links -->
<a name="TOP"><b>Up</b></a><b>:</b>
<a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><img
src="qh--dt.gif" alt="[delaunay]" align="middle" width="100"
height="100"></a>qdelaunay -- Delaunay triangulation</h1>
<p>The Delaunay triangulation is the triangulation with empty
circumspheres. It has many useful properties and applications.
See the survey article by Aurenhammer [<a
href="index.htm#aure91">'91</a>] and the detailed introduction
by O'Rourke [<a href="index.htm#orou94">'94</a>]. </p>
<blockquote>
<dl>
<dt><b>Example:</b> rbox r y c G0.1 D2 | qdelaunay <a href="qh-opto.htm#s">s</a>
<a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optt.htm#TO">TO
result</a></dt>
<dd>Compute the 2-d Delaunay triangulation of a triangle and
a small square.
Write a summary to the console and unoriented regions to 'result'.
Merge regions for cocircular input sites (i.e., the
square).</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox r y c G0.1 D2 | qdelaunay <a href="qh-opto.htm#s">s</a>
<a href="qh-optf.htm#Fv">Fv</a> <a href="qh-optq.htm#Qt">Qt</a></dt>
<dd>Compute the 2-d Delaunay triangulation of a triangle and
a small square. Write a summary and unoriented
regions to the console. Produce triangulated output.</dd>
<dt>&nbsp;</dt>
<dt><b>Example:</b> rbox 10 D2 | qdelaunay <a
href="qh-optq.htm#QJn">QJ</a> <a href="qh-opto.htm#s">s</a>
<a href="qh-opto.htm#i">i</a> <a href="qh-optt.htm#TO">TO
result</a></dt>
<dd>Compute the 2-d Delaunay triangulation of 10 random
points. Joggle the input to guarantee triangular output.
Write a summary to the console and the regions to
'result'.</dd>
</dl>
</blockquote>
<p>Qhull computes the Delaunay triangulation by computing a
convex hull. It lifts the input sites to a paraboloid by adding
the sum of the squares of the coordinates. It scales the height
of the paraboloid to improve numeric precision ('<a href=qh-optq.htm#Qbb>Qbb</a>').
It computes the convex
hull of the lifted sites, and projects the lower convex hull to
the input.
<p>Each region of the Delaunay triangulation
corresponds to a facet of the lower half of the convex hull.
Facets of the upper half of the convex hull correspond to the <a
href="qdelau_f.htm">furthest-site Delaunay triangulation</a>.
See the examples, <a href="qh-eg.htm#delaunay">Delaunay and
Voronoi diagrams</a>.</p>
<p>See <a href="http://www.qhull.org/html/qh-faq.htm#TOC">Qhull FAQ</a> - Delaunay and
Voronoi diagram questions.</p>
<p>By default, qdelaunay merges cocircular and cospherical regions.
For example, the Delaunay triangulation of a square inside a diamond
('rbox D2 c d G4 | qdelaunay') contains one region for the square.
<p>Use option '<a href="qh-optq.htm#Qz">Qz</a>' if the input is circular, cospherical, or
nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid.
<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
all Delaunay regions will be simplicial (e.g., triangles in 2-d).
Some regions may be
degenerate and have zero area. Triangulated output identifies coincident
points.
<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input), all Delaunay regions
will be simplicial (e.g., triangles in 2-d). Coincident points will
create small regions since the points are joggled apart. Joggled input
is less accurate than triangulated output ('Qt'). See <a
href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
<p>The output for 3-d Delaunay triangulations may be confusing if the
input contains cospherical data. See the FAQ item
<a href=qh-faq.htm#extra>Why
are there extra points in a 4-d or higher convex hull?</a>
Avoid these problems with triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or
joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
</p>
<p>The 'qdelaunay' program is equivalent to
'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a>' in 2-d to 3-d, and
'<a href=qhull.htm#outputs>qhull d</a> <a href=qh-optq.htm#Qbb>Qbb</a> <a href=qh-optq.htm#Qx>Qx</a>'
in 4-d and higher. It disables the following Qhull
<a href=qh-quick.htm#options>options</a>: <i>d n v H U Qb QB Qc Qf Qg Qi
Qm Qr QR Qv Qx TR E V FC Fi Fo Fp Ft FV Q0,etc</i>.
<p><b>Copyright &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h3><a href="#TOP">&#187;</a><a name="synopsis">qdelaunay synopsis</a></h3>
<pre>
qdelaunay- compute the Delaunay triangulation.
input (stdin): dimension, number of points, point coordinates
comments start with a non-numeric character
options (qdelaun.htm):
Qt - triangulated output
QJ - joggle input instead of merging facets
Qu - furthest-site Delaunay triangulation
Tv - verify result: structure, convexity, and in-circle test
. - concise list of all options
- - one-line description of all options
output options (subset):
s - summary of results (default)
i - vertices incident to each Delaunay region
Fx - extreme points (vertices of the convex hull)
o - OFF format (shows the points lifted to a paraboloid)
G - Geomview output (2-d and 3-d points lifted to a paraboloid)
m - Mathematica output (2-d inputs lifted to a paraboloid)
QVn - print Delaunay regions that include point n, -n if not
TO file- output results to file, may be enclosed in single quotes
examples:
rbox c P0 D2 | qdelaunay s o rbox c P0 D2 | qdelaunay i
rbox c P0 D3 | qdelaunay Fv Qt rbox c P0 D2 | qdelaunay s Qu Fv
rbox c G1 d D2 | qdelaunay s i rbox c G1 d D2 | qdelaunay s i Qt
rbox M3,4 z 100 D2 | qdelaunay s rbox M3,4 z 100 D2 | qdelaunay s Qt
</pre>
<h3><a href="#TOP">&#187;</a><a name="input">qdelaunay
input</a></h3>
<blockquote>
<p>The input data on <tt>stdin</tt> consists of:</p>
<ul>
<li>dimension
<li>number of points</li>
<li>point coordinates</li>
</ul>
<p>Use I/O redirection (e.g., qdelaunay &lt; data.txt), a pipe (e.g., rbox 10 | qdelaunay),
or the '<a href=qh-optt.htm#TI>TI</a>' option (e.g., qdelaunay TI data.txt).
<p>For example, this is four cocircular points inside a square. Its Delaunay
triangulation contains 8 triangles and one four-sided
figure.
<p>
<blockquote>
<tt>rbox s 4 W0 c G1 D2 &gt; data</tt>
<blockquote><pre>
2 RBOX s 4 W0 c D2
8
-0.4941988586954018 -0.07594397977563715
-0.06448037284989526 0.4958248496365813
0.4911154367094632 0.09383830681375946
-0.348353580869097 -0.3586778257652367
-1 -1
-1 1
1 -1
1 1
</pre></blockquote>
<p><tt>qdelaunay s i &lt; data</tt>
<blockquote><pre>
Delaunay triangulation by the convex hull of 8 points in 3-d
Number of input sites: 8
Number of Delaunay regions: 9
Number of non-simplicial Delaunay regions: 1
Statistics for: RBOX s 4 W0 c D2 | QDELAUNAY s i
Number of points processed: 8
Number of hyperplanes created: 18
Number of facets in hull: 10
Number of distance tests for qhull: 33
Number of merged facets: 2
Number of distance tests for merging: 102
CPU seconds to compute hull (after input): 0.028
9
1 7 5
6 3 4
2 3 6
7 2 6
2 7 1
0 5 4
3 0 4
0 1 5
1 0 3 2
</pre></blockquote>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="outputs">qdelaunay
outputs</a></h3>
<blockquote>
<p>These options control the output of Delaunay triangulations:</p>
<blockquote>
<dl compact>
<dd><b>Delaunay regions</b></dd>
<dt><a href="qh-opto.htm#i">i</a></dt>
<dd>list input sites for each Delaunay region. The first line is the number of regions. The
remaining lines list the input sites for each region. The regions are
oriented. In 3-d and
higher, report cospherical sites by adding extra points. Use triangulated
output ('<a href="qh-optq.htm#Qt">Qt</a>') to avoid non-simpicial regions. For the circle-in-square example,
eight Delaunay regions are triangular and the ninth has four input sites.</dd>
<dt><a href="qh-optf.htm#Fv">Fv</a></dt>
<dd>list input sites for each Delaunay region. The first line is the number of regions.
Each remaining line starts with the number of input sites. The regions
are unoriented. For the circle-in-square example,
eight Delaunay regions are triangular and the ninth has four input sites.</dd>
<dt><a href="qh-optf.htm#Fn">Fn</a></dt>
<dd>list neighboring regions for each Delaunay region. The first line is the
number of regions. Each remaining line starts with the number of
neighboring regions. Negative indices (e.g., <em>-1</em>) indicate regions
outside of the Delaunay triangulation.
For the circle-in-square example, the four regions on the square are neighbors to
the region-at-infinity.</dd>
<dt><a href="qh-optf.htm#FN">FN</a></dt>
<dd>list the Delaunay regions for each input site. The first line is the
total number of input sites. Each remaining line starts with the number of
Delaunay regions. Negative indices (e.g., <em>-1</em>) indicate regions
outside of the Delaunay triangulation.
For the circle-in-square example, each point on the circle belongs to four
Delaunay regions. Use '<a href="qh-optq.htm#Qc">Qc</a> FN'
to include coincident input sites and deleted vertices. </dd>
<dt><a href="qh-optf.htm#Fa">Fa</a></dt>
<dd>print area for each Delaunay region. The first line is the number of regions.
The areas follow, one line per region. For the circle-in-square example, the
cocircular region has area 0.4. </dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Input sites</b></dd>
<dt><a href="qh-optf.htm#Fc">Fc</a></dt>
<dd>list coincident input sites for each Delaunay region.
The first line is the number of regions. The remaining lines start with
the number of coincident sites and deleted vertices. Deleted vertices
indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
A coincident site is assigned to one Delaunay
region. Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'Fc'; the joggle will separate
coincident sites.</dd>
<dt><a href="qh-optf.htm#FP">FP</a></dt>
<dd>print coincident input sites with distance to
nearest site (i.e., vertex). The first line is the
number of coincident sites. Each remaining line starts with the point ID of
an input site, followed by the point ID of a coincident point, its region, and distance.
Includes deleted vertices which
indicate highly degenerate input (see'<a href="qh-optf.htm#Fs">Fs</a>').
Do not use '<a href="qh-optq.htm#QJn">QJ</a>' with 'FP'; the joggle will separate
coincident sites.</dd>
<dt><a href="qh-optf.htm#Fx">Fx</a></dt>
<dd>list extreme points of the input sites. These points are on the
boundary of the convex hull. The first line is the number of
extreme points. Each point is listed, one per line. The circle-in-square example
has four extreme points.</dd>
<dt>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>General</b></dd>
<dt><a href="qh-optf.htm#FA">FA</a></dt>
<dd>compute total area for '<a href="qh-opto.htm#s">s</a>'
and '<a href="qh-optf.htm#FS">FS</a>'</dd>
<dt><a href="qh-opto.htm#o">o</a></dt>
<dd>print lower facets of the corresponding convex hull (a
paraboloid)</dd>
<dt><a href="qh-opto.htm#m">m</a></dt>
<dd>Mathematica output for the lower facets of the paraboloid (2-d triangulations).</dd>
<dt><a href="qh-optf.htm#FM">FM</a></dt>
<dd>Maple output for the lower facets of the paraboloid (2-d triangulations).</dd>
<dt><a href="qh-optg.htm#G">G</a></dt>
<dd>Geomview output for the paraboloid (2-d or 3-d triangulations).</dd>
<dt><a href="qh-opto.htm#s">s</a></dt>
<dd>print summary for the Delaunay triangulation. Use '<a
href="qh-optf.htm#Fs">Fs</a>' and '<a
href="qh-optf.htm#FS">FS</a>' for numeric data.</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="controls">qdelaunay
controls</a></h3>
<blockquote>
<p>These options provide additional control:</p>
<blockquote>
<dl compact>
<dt><a href="qh-optq.htm#Qt">Qt</a></dt>
<dd>triangulated output. Qhull triangulates non-simplicial facets. It may produce
degenerate facets of zero area.</dd>
<dt><a href="qh-optq.htm#QJn">QJ</a></dt>
<dd>joggle the input to avoid cospherical and coincident
sites. It is less accurate than triangulated output ('Qt').</dd>
<dt><a href="qh-optq.htm#Qu">Qu</a></dt>
<dd>compute the <a href="qdelau_f.htm">furthest-site Delaunay triangulation</a>.</dd>
<dt><a href="qh-optq.htm#Qz">Qz</a></dt>
<dd>add a point above the paraboloid to reduce precision
errors. Use it for nearly cocircular/cospherical input
(e.g., 'rbox c | qdelaunay Qz'). The point is printed for
options '<a href="qh-optf.htm#Ft">Ft</a>' and '<a
href="qh-opto.htm#o">o</a>'.</dd>
<dt><a href="qh-optq.htm#QVn">QVn</a></dt>
<dd>select facets adjacent to input site <em>n</em> (marked
'good').</dd>
<dt><a href="qh-optt.htm#Tv">Tv</a></dt>
<dd>verify result.</dd>
<dt><a href="qh-optt.htm#TO">TI file</a></dt>
<dd>input data from file. The filename may not use spaces or quotes.</dd>
<dt><a href="qh-optt.htm#TO">TO file</a></dt>
<dd>output results to file. Use single quotes if the filename
contains spaces (e.g., <tt>TO 'file with spaces.txt'</tt></dd>
<dt><a href="qh-optt.htm#TFn">TFn</a></dt>
<dd>report progress after constructing <em>n</em> facets</dd>
<dt><a href="qh-optp.htm#PDk">PDk:1</a></dt>
<dd>include upper and lower facets in the output. Set <em>k</em>
to the last dimension (e.g., 'PD2:1' for 2-d inputs). </dd>
<dt><a href="qh-opto.htm#f">f</a></dt>
<dd>facet dump. Print the data structure for each facet (i.e., Delaunay region).</dd>
</dl>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="graphics">qdelaunay
graphics</a></h3>
<blockquote>
<p>For 2-d and 3-d Delaunay triangulations, Geomview ('qdelaunay <a
href="qh-optg.htm#G">G</a>') displays the corresponding convex
hull (a paraboloid). </p>
<p>To view a 2-d Delaunay triangulation, use 'qdelaunay <a
href="qh-optg.htm#GDn">GrD2</a>' to drop the last dimension. This
is the same as viewing the hull without perspective (see
Geomview's 'cameras' menu). </p>
<p>To view a 3-d Delaunay triangulation, use 'qdelaunay <a
href="qh-optg.htm#GDn">GrD3</a>' to drop the last dimension. You
may see extra edges. These are interior edges that Geomview moves
towards the viewer (see 'lines closer' in Geomview's camera
options). Use option '<a href="qh-optg.htm#Gt">Gt</a>' to make
the outer ridges transparent in 3-d. See <a
href="qh-eg.htm#delaunay">Delaunay and Voronoi examples</a>.</p>
<p>For 2-d Delaunay triangulations, Mathematica ('<a
href="qh-opto.htm#m">m</a>') and Maple ('<a
href="qh-optf.htm#FM">FM</a>') output displays the lower facets of the corresponding convex
hull (a paraboloid). </p>
<p>For 2-d, furthest-site Delaunay triangulations, Maple and Mathematica output ('<a
href="qh-optq.htm#Qu">Qu</a> <a
href="qh-opto.htm#m">m</a>') displays the upper facets of the corresponding convex
hull (a paraboloid). </p>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="notes">qdelaunay
notes</a></h3>
<blockquote>
<p>You can simplify the Delaunay triangulation by enclosing the input
sites in a large square or cube. This is particularly recommended
for cocircular or cospherical input data.
<p>A non-simplicial Delaunay region indicates nearly cocircular or
cospherical input sites. To avoid non-simplicial regions either triangulate
the output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggle
the input ('<a href="qh-optq.htm#QJn">QJ</a>'). Triangulated output
is more accurate than joggled input. Alternatively, use an <a
href="qh-impre.htm#exact">exact arithmetic code</a>.</p>
<p>Delaunay triangulations do not include facets that are
coplanar with the convex hull of the input sites. A facet is
coplanar if the last coefficient of its normal is
nearly zero (see <a href="../src/libqhull/user.h#ZEROdelaunay">qh_ZEROdelaunay</a>).
<p>See <a href=qh-impre.htm#delaunay>Imprecision issues :: Delaunay triangulations</a>
for a discussion of precision issues. Deleted vertices indicate
highly degenerate input. They are listed in the summary output and
option '<a href="qh-optf.htm#Fs">Fs</a>'.</p>
<p>To compute the Delaunay triangulation of points on a sphere,
compute their convex hull. If the sphere is the unit sphere at
the origin, the facet normals are the Voronoi vertices of the
input. The points may be restricted to a hemisphere. [S. Fortune]
</p>
<p>The 3-d Delaunay triangulation of regular points on a half
spiral (e.g., 'rbox 100 l | qdelaunay') has quadratic size, while the Delaunay triangulation
of random 3-d points is
approximately linear for reasonably sized point sets.
<p>With the <a href="qh-code.htm#library">Qhull library</a>, you
can use <tt>qh_findbestfacet</tt> in <tt>poly2.c</tt> to locate the facet
that contains a point. You should first lift the point to the
paraboloid (i.e., the last coordinate is the sum of the squares
of the point's coordinates -- <tt>qh_setdelaunay</tt>). Do not use options
'<a href="qh-optq.htm#Qbb">Qbb</a>', '<a href="qh-optq.htm#QbB">QbB</a>',
'<a href="qh-optq.htm#Qbk">Qbk:n</a>', or '<a
href="qh-optq.htm#QBk">QBk:n</a>' since these scale the last
coordinate. </p>
<p>If a point is interior to the convex hull of the input set, it
is interior to the adjacent vertices of the Delaunay
triangulation. This is demonstrated by the following pipe for
point 0:
<pre>
qdelaunay &lt;data s FQ QV0 p | qconvex s Qb3:0B3:0 p
</pre>
<p>The first call to qdelaunay returns the neighboring points of
point 0 in the Delaunay triangulation. The second call to qconvex
returns the vertices of the convex hull of these points (after
dropping the lifted coordinate). If point 0 is interior to the
original point set, it is interior to the reduced point set. </p>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="conventions">qdelaunay conventions</a></h3>
<blockquote>
<p>The following terminology is used for Delaunay triangulations
in Qhull for dimension <i>d</i>. The underlying structure is the
lower facets of a convex hull in dimension <i>d+1</i>. For
further information, see <a href="index.htm#structure">data
structures</a> and <a href="qconvex.htm#conventions">convex hull
conventions</a>.</p>
<blockquote>
<ul>
<li><em>input site</em> - a point in the input (one dimension
lower than a point on the convex hull)</li>
<li><em>point</em> - a point has <i>d+1</i> coordinates. The
last coordinate is the sum of the squares of the input
site's coordinates</li>
<li><em>coplanar point</em> - a <em>coincident</em>
input site or a deleted vertex. Deleted vertices
indicate highly degenerate input.</li>
<li><em>vertex</em> - a point on the paraboloid. It
corresponds to a unique input site. </li>
<li><em>point-at-infinity</em> - a point added above the
paraboloid by option '<a href="qh-optq.htm#Qz">Qz</a>'</li>
<li><em>lower facet</em> - a facet corresponding to a
Delaunay region. The last coefficient of its normal is
clearly negative.</li>
<li><em>upper facet</em> - a facet corresponding to a
furthest-site Delaunay region. The last coefficient of
its normal is clearly positive. </li>
<li><em>Delaunay region</em> - a
lower facet projected to the input sites</li>
<li><em>upper Delaunay region</em> - an upper facet projected
to the input sites</li>
<li><em>non-simplicial facet</em> - more than <em>d</em>
input sites are cocircular or cospherical</li>
<li><em>good facet</em> - a Delaunay region with optional
restrictions by '<a href="qh-optq.htm#QVn">QVn</a>', etc.</li>
</ul>
</blockquote>
</blockquote>
<h3><a href="#TOP">&#187;</a><a name="options">qdelaunay options</a></h3>
<pre>
qdelaunay- compute the Delaunay triangulation
http://www.qhull.org
input (stdin):
first lines: dimension and number of points (or vice-versa).
other lines: point coordinates, best if one point per line
comments: start with a non-numeric character
options:
Qt - triangulated output
QJ - joggle input instead of merging facets
Qu - compute furthest-site Delaunay triangulation
Qhull control options:
QJn - randomly joggle input in range [-n,n]
Qs - search all points for the initial simplex
Qz - add point-at-infinity to Delaunay triangulation
QGn - print Delaunay region if visible from point n, -n if not
QVn - print Delaunay regions that include point n, -n if not
Trace options:
T4 - trace at level n, 4=all, 5=mem/gauss, -1= events
Tc - check frequently during execution
Ts - print statistics
Tv - verify result: structure, convexity, and in-circle test
Tz - send all output to stdout
TFn - report summary when n or more facets created
TI file - input data from file, no spaces or single quotes
TO file - output results to file, may be enclosed in single quotes
TPn - turn on tracing when point n added to hull
TMn - turn on tracing at merge n
TWn - trace merge facets when width > n
TVn - stop qhull after adding point n, -n for before (see TCn)
TCn - stop qhull after building cone for point n (see TVn)
Precision options:
Cn - radius of centrum (roundoff added). Merge facets if non-convex
An - cosine of maximum angle. Merge facets if cosine > n or non-convex
C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
Rn - randomly perturb computations by a factor of [1-n,1+n]
Wn - min facet width for outside point (before roundoff)
Output formats (may be combined; if none, produces a summary to stdout):
f - facet dump
G - Geomview output (see below)
i - vertices incident to each Delaunay region
m - Mathematica output (2-d only, lifted to a paraboloid)
o - OFF format (dim, points, and facets as a paraboloid)
p - point coordinates (lifted to a paraboloid)
s - summary (stderr)
More formats:
Fa - area for each Delaunay region
FA - compute total area for option 's'
Fc - count plus coincident points for each Delaunay region
Fd - use cdd format for input (homogeneous with offset first)
FD - use cdd format for numeric output (offset first)
FF - facet dump without ridges
FI - ID of each Delaunay region
Fm - merge count for each Delaunay region (511 max)
FM - Maple output (2-d only, lifted to a paraboloid)
Fn - count plus neighboring region for each Delaunay region
FN - count plus neighboring region for each point
FO - options and precision constants
FP - nearest point and distance for each coincident point
FQ - command used for qdelaunay
Fs - summary: #int (8), dimension, #points, tot vertices, tot facets,
for output: #vertices, #Delaunay regions,
#coincident points, #non-simplicial regions
#real (2), max outer plane, min vertex
FS - sizes: #int (0)
#real (2), tot area, 0
Fv - count plus vertices for each Delaunay region
Fx - extreme points of Delaunay triangulation (on convex hull)
Geomview options (2-d and 3-d)
Ga - all points as dots
Gp - coplanar points and vertices as radii
Gv - vertices as spheres
Gi - inner planes only
Gn - no planes
Go - outer planes only
Gc - centrums
Gh - hyperplane intersections
Gr - ridges
GDn - drop dimension n in 3-d and 4-d output
Gt - transparent outer ridges to view 3-d Delaunay
Print options:
PAn - keep n largest Delaunay regions by area
Pdk:n - drop facet if normal[k] &lt;= n (default 0.0)
PDk:n - drop facet if normal[k] >= n
Pg - print good Delaunay regions (needs 'QGn' or 'QVn')
PFn - keep Delaunay regions whose area is at least n
PG - print neighbors of good regions (needs 'QGn' or 'QVn')
PMn - keep n Delaunay regions with most merges
Po - force output. If error, output neighborhood of facet
Pp - do not report precision problems
. - list of all options
- - one line descriptions of all options
</pre>
<!-- Navigation links -->
<hr>
<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis
&#149; <a href="#input">in</a>put &#149; <a href="#outputs">ou</a>tputs
&#149; <a href="#controls">co</a>ntrols &#149; <a href="#graphics">gr</a>aphics
&#149; <a href="#notes">no</a>tes &#149; <a href="#conventions">co</a>nventions
&#149; <a href="#options">op</a>tions
<!-- GC common information -->
<hr>
<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>
<p>Comments to: <a href=mailto:qhull@qhull.org>qhull@qhull.org</a>
</a><br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more