Merge remote-tracking branch 'remotes/origin/master' into support_improvements
@@ -1,3 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
|
||||
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
|
||||
@@ -13,67 +14,74 @@ endif()
|
||||
|
||||
|
||||
set(AVRDUDE_SOURCES
|
||||
${LIBDIR}/avrdude/arduino.c
|
||||
${LIBDIR}/avrdude/avr.c
|
||||
# ${LIBDIR}/avrdude/avrftdi.c
|
||||
# ${LIBDIR}/avrdude/avrftdi_tpi.c
|
||||
${LIBDIR}/avrdude/avrpart.c
|
||||
${LIBDIR}/avrdude/avr910.c
|
||||
${LIBDIR}/avrdude/bitbang.c
|
||||
${LIBDIR}/avrdude/buspirate.c
|
||||
${LIBDIR}/avrdude/butterfly.c
|
||||
${LIBDIR}/avrdude/config.c
|
||||
${LIBDIR}/avrdude/config_gram.c
|
||||
# ${LIBDIR}/avrdude/confwin.c
|
||||
${LIBDIR}/avrdude/crc16.c
|
||||
# ${LIBDIR}/avrdude/dfu.c
|
||||
${LIBDIR}/avrdude/fileio.c
|
||||
# ${LIBDIR}/avrdude/flip1.c
|
||||
# ${LIBDIR}/avrdude/flip2.c
|
||||
# ${LIBDIR}/avrdude/ft245r.c
|
||||
# ${LIBDIR}/avrdude/jtagmkI.c
|
||||
# ${LIBDIR}/avrdude/jtagmkII.c
|
||||
# ${LIBDIR}/avrdude/jtag3.c
|
||||
${LIBDIR}/avrdude/lexer.c
|
||||
${LIBDIR}/avrdude/linuxgpio.c
|
||||
${LIBDIR}/avrdude/lists.c
|
||||
# ${LIBDIR}/avrdude/par.c
|
||||
${LIBDIR}/avrdude/pgm.c
|
||||
${LIBDIR}/avrdude/pgm_type.c
|
||||
${LIBDIR}/avrdude/pickit2.c
|
||||
${LIBDIR}/avrdude/pindefs.c
|
||||
# ${LIBDIR}/avrdude/ppi.c
|
||||
# ${LIBDIR}/avrdude/ppiwin.c
|
||||
${LIBDIR}/avrdude/safemode.c
|
||||
${LIBDIR}/avrdude/ser_avrdoper.c
|
||||
${LIBDIR}/avrdude/serbb_posix.c
|
||||
${LIBDIR}/avrdude/serbb_win32.c
|
||||
${LIBDIR}/avrdude/ser_posix.c
|
||||
${LIBDIR}/avrdude/ser_win32.c
|
||||
${LIBDIR}/avrdude/stk500.c
|
||||
${LIBDIR}/avrdude/stk500generic.c
|
||||
${LIBDIR}/avrdude/stk500v2.c
|
||||
${LIBDIR}/avrdude/term.c
|
||||
${LIBDIR}/avrdude/update.c
|
||||
# ${LIBDIR}/avrdude/usbasp.c
|
||||
# ${LIBDIR}/avrdude/usb_hidapi.c
|
||||
# ${LIBDIR}/avrdude/usb_libusb.c
|
||||
# ${LIBDIR}/avrdude/usbtiny.c
|
||||
${LIBDIR}/avrdude/wiring.c
|
||||
arduino.c
|
||||
avr.c
|
||||
# avrftdi.c
|
||||
# avrftdi_tpi.c
|
||||
avrpart.c
|
||||
avr910.c
|
||||
bitbang.c
|
||||
buspirate.c
|
||||
butterfly.c
|
||||
config.c
|
||||
config_gram.c
|
||||
# confwin.c
|
||||
crc16.c
|
||||
# dfu.c
|
||||
fileio.c
|
||||
# flip1.c
|
||||
# flip2.c
|
||||
# ft245r.c
|
||||
# jtagmkI.c
|
||||
# jtagmkII.c
|
||||
# jtag3.c
|
||||
lexer.c
|
||||
linuxgpio.c
|
||||
lists.c
|
||||
# par.c
|
||||
pgm.c
|
||||
pgm_type.c
|
||||
pickit2.c
|
||||
pindefs.c
|
||||
# ppi.c
|
||||
# ppiwin.c
|
||||
safemode.c
|
||||
ser_avrdoper.c
|
||||
serbb_posix.c
|
||||
serbb_win32.c
|
||||
ser_posix.c
|
||||
ser_win32.c
|
||||
stk500.c
|
||||
stk500generic.c
|
||||
stk500v2.c
|
||||
term.c
|
||||
update.c
|
||||
# usbasp.c
|
||||
# usb_hidapi.c
|
||||
# usb_libusb.c
|
||||
# usbtiny.c
|
||||
wiring.c
|
||||
|
||||
${LIBDIR}/avrdude/main.c
|
||||
${LIBDIR}/avrdude/avrdude-slic3r.hpp
|
||||
${LIBDIR}/avrdude/avrdude-slic3r.cpp
|
||||
main.c
|
||||
avrdude-slic3r.hpp
|
||||
avrdude-slic3r.cpp
|
||||
)
|
||||
if (WIN32)
|
||||
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
|
||||
${LIBDIR}/avrdude/windows/unistd.cpp
|
||||
${LIBDIR}/avrdude/windows/getopt.c
|
||||
windows/unistd.cpp
|
||||
windows/getopt.c
|
||||
)
|
||||
endif()
|
||||
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
||||
|
||||
set(STANDALONE_SOURCES
|
||||
main-standalone.c
|
||||
)
|
||||
add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
|
||||
target_link_libraries(avrdude-slic3r avrdude)
|
||||
set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
|
||||
target_include_directories(avrdude SYSTEM PRIVATE ${LIBDIR}/avrdude/windows) # So that sources find the getopt.h windows drop-in
|
||||
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
|
||||
endif()
|
||||
|
||||
54
xs/src/avrdude/Makefile.standalone
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
TARGET = avrdude-slic3r
|
||||
|
||||
SOURCES = \
|
||||
arduino.c \
|
||||
avr.c \
|
||||
avrpart.c \
|
||||
avr910.c \
|
||||
bitbang.c \
|
||||
buspirate.c \
|
||||
butterfly.c \
|
||||
config.c \
|
||||
config_gram.c \
|
||||
crc16.c \
|
||||
fileio.c \
|
||||
lexer.c \
|
||||
linuxgpio.c \
|
||||
lists.c \
|
||||
pgm.c \
|
||||
pgm_type.c \
|
||||
pickit2.c \
|
||||
pindefs.c \
|
||||
safemode.c \
|
||||
ser_avrdoper.c \
|
||||
serbb_posix.c \
|
||||
serbb_win32.c \
|
||||
ser_posix.c \
|
||||
ser_win32.c \
|
||||
stk500.c \
|
||||
stk500generic.c \
|
||||
stk500v2.c \
|
||||
term.c \
|
||||
update.c \
|
||||
wiring.c \
|
||||
main.c \
|
||||
main-standalone.c
|
||||
|
||||
OBJECTS = $(SOURCES:.c=.o)
|
||||
CFLAGS = -std=c99 -Wall -D_BSD_SOURCE -D_DEFAULT_SOURCE -O3 -DNDEBUG -fPIC
|
||||
LDFLAGS = -lm
|
||||
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) -o ./$@ $(OBJECTS) $(LDFLAGS)
|
||||
|
||||
$(OBJECTS): %.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
$(RM) -f $(OBJECTS) $(TARGET)
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
#include <exception>
|
||||
|
||||
extern "C" {
|
||||
#include "ac_cfg.h"
|
||||
@@ -28,6 +32,11 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
|
||||
(*progress_fn)(task, progress);
|
||||
}
|
||||
|
||||
static void avrdude_oom_handler(const char *context, void *user_p)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
@@ -35,6 +44,8 @@ struct AvrDude::priv
|
||||
{
|
||||
std::string sys_config;
|
||||
std::deque<std::vector<std::string>> args;
|
||||
bool cancelled = false;
|
||||
int exit_code = 0;
|
||||
size_t current_args_set = 0;
|
||||
RunFn run_fn;
|
||||
MessageFn message_fn;
|
||||
@@ -45,16 +56,22 @@ struct AvrDude::priv
|
||||
|
||||
priv(std::string &&sys_config) : sys_config(sys_config) {}
|
||||
|
||||
void set_handlers();
|
||||
void unset_handlers();
|
||||
int run_one(const std::vector<std::string> &args);
|
||||
int run();
|
||||
|
||||
struct HandlerGuard
|
||||
{
|
||||
priv &p;
|
||||
|
||||
HandlerGuard(priv &p) : p(p) { p.set_handlers(); }
|
||||
~HandlerGuard() { p.unset_handlers(); }
|
||||
};
|
||||
};
|
||||
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
}
|
||||
|
||||
void AvrDude::priv::set_handlers()
|
||||
{
|
||||
if (message_fn) {
|
||||
::avrdude_message_handler_set(avrdude_message_handler_closure, reinterpret_cast<void*>(&message_fn));
|
||||
} else {
|
||||
@@ -67,10 +84,27 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
::avrdude_progress_handler_set(nullptr, nullptr);
|
||||
}
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
::avrdude_oom_handler_set(avrdude_oom_handler, nullptr);
|
||||
}
|
||||
|
||||
void AvrDude::priv::unset_handlers()
|
||||
{
|
||||
::avrdude_message_handler_set(nullptr, nullptr);
|
||||
::avrdude_progress_handler_set(nullptr, nullptr);
|
||||
::avrdude_oom_handler_set(nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
}
|
||||
|
||||
HandlerGuard guard(*this);
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -132,7 +166,7 @@ AvrDude& AvrDude::on_complete(CompleteFn fn)
|
||||
|
||||
int AvrDude::run_sync()
|
||||
{
|
||||
return p->run();
|
||||
return p ? p->run() : -1;
|
||||
}
|
||||
|
||||
AvrDude::Ptr AvrDude::run()
|
||||
@@ -141,14 +175,46 @@ AvrDude::Ptr AvrDude::run()
|
||||
|
||||
if (self->p) {
|
||||
auto avrdude_thread = std::thread([self]() {
|
||||
if (self->p->run_fn) {
|
||||
self->p->run_fn();
|
||||
}
|
||||
try {
|
||||
if (self->p->run_fn) {
|
||||
self->p->run_fn(self);
|
||||
}
|
||||
|
||||
auto res = self->p->run();
|
||||
if (! self->p->cancelled) {
|
||||
self->p->exit_code = self->p->run();
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn(res, self->p->current_args_set);
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
self->p->exit_code = EXIT_EXCEPTION;
|
||||
|
||||
static const char *msg = "An exception was thrown in the background thread:\n";
|
||||
|
||||
const char *what = ex.what();
|
||||
auto &message_fn = self->p->message_fn;
|
||||
if (message_fn) {
|
||||
message_fn(msg, sizeof(msg));
|
||||
message_fn(what, std::strlen(what));
|
||||
message_fn("\n", 1);
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
} catch (...) {
|
||||
self->p->exit_code = EXIT_EXCEPTION;
|
||||
|
||||
static const char *msg = "An unkown exception was thrown in the background thread.\n";
|
||||
|
||||
if (self->p->message_fn) {
|
||||
self->p->message_fn(msg, sizeof(msg));
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -160,7 +226,10 @@ AvrDude::Ptr AvrDude::run()
|
||||
|
||||
void AvrDude::cancel()
|
||||
{
|
||||
::avrdude_cancel();
|
||||
if (p) {
|
||||
p->cancelled = true;
|
||||
::avrdude_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void AvrDude::join()
|
||||
@@ -170,5 +239,20 @@ void AvrDude::join()
|
||||
}
|
||||
}
|
||||
|
||||
bool AvrDude::cancelled()
|
||||
{
|
||||
return p ? p->cancelled : false;
|
||||
}
|
||||
|
||||
int AvrDude::exit_code()
|
||||
{
|
||||
return p ? p->exit_code : 0;
|
||||
}
|
||||
|
||||
size_t AvrDude::last_args_set()
|
||||
{
|
||||
return p ? p->current_args_set : 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,11 +11,16 @@ namespace Slic3r {
|
||||
class AvrDude
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
EXIT_SUCCEESS = 0,
|
||||
EXIT_EXCEPTION = -1000,
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AvrDude> Ptr;
|
||||
typedef std::function<void()> RunFn;
|
||||
typedef std::function<void(Ptr /* avrdude */)> RunFn;
|
||||
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
|
||||
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
||||
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
|
||||
// Main c-tor, sys_config is the location of avrdude's main configuration file
|
||||
AvrDude(std::string sys_config);
|
||||
@@ -31,7 +36,8 @@ public:
|
||||
AvrDude& push_args(std::vector<std::string> args);
|
||||
|
||||
// Set a callback to be called just after run() before avrdude is ran
|
||||
// This can be used to perform any needed setup tasks from the background thread.
|
||||
// This can be used to perform any needed setup tasks from the background thread,
|
||||
// and, optionally, to cancel by writing true to the `cancel` argument.
|
||||
// This has no effect when using run_sync().
|
||||
AvrDude& on_run(RunFn fn);
|
||||
|
||||
@@ -48,11 +54,23 @@ public:
|
||||
// This has no effect when using run_sync().
|
||||
AvrDude& on_complete(CompleteFn fn);
|
||||
|
||||
// Perform AvrDude invocation(s) synchronously on the current thread
|
||||
int run_sync();
|
||||
|
||||
// Perform AvrDude invocation(s) on a background thread.
|
||||
// Current instance is moved into a shared_ptr which is returned (and also passed in on_run, if any).
|
||||
Ptr run();
|
||||
|
||||
// Cancel current operation
|
||||
void cancel();
|
||||
|
||||
// If there is a background thread and it is joinable, join() it,
|
||||
// that is, wait for it to finish.
|
||||
void join();
|
||||
|
||||
bool cancelled(); // Whether avrdude run was cancelled
|
||||
int exit_code(); // The exit code of the last invocation
|
||||
size_t last_args_set(); // Index of the last argument set that was processsed
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
@@ -39,6 +39,12 @@ typedef void (*avrdude_progress_handler_t)(const char *task, unsigned progress,
|
||||
void avrdude_progress_handler_set(avrdude_progress_handler_t newhandler, void *user_p);
|
||||
void avrdude_progress_external(const char *task, unsigned progress);
|
||||
|
||||
// OOM handler
|
||||
typedef void (*avrdude_oom_handler_t)(const char *context, void *user_p);
|
||||
void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p);
|
||||
void avrdude_oom(const char *context);
|
||||
|
||||
|
||||
// Cancellation
|
||||
void avrdude_cancel();
|
||||
|
||||
|
||||
@@ -36,8 +36,9 @@ OPCODE * avr_new_opcode(void)
|
||||
|
||||
m = (OPCODE *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_new_opcode(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
@@ -56,8 +57,9 @@ static OPCODE * avr_dup_opcode(OPCODE * op)
|
||||
|
||||
m = (OPCODE *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_opcode(): out of memory\n");
|
||||
}
|
||||
|
||||
memcpy(m, op, sizeof(*m));
|
||||
@@ -249,8 +251,9 @@ AVRMEM * avr_new_memtype(void)
|
||||
|
||||
m = (AVRMEM *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_new_memtype(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
@@ -300,9 +303,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
|
||||
if (m->buf != NULL) {
|
||||
n->buf = (unsigned char *)malloc(n->size);
|
||||
if (n->buf == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
n->size);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
// n->size);
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_mem(): out of memory");
|
||||
}
|
||||
memcpy(n->buf, m->buf, n->size);
|
||||
}
|
||||
@@ -310,9 +314,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
|
||||
if (m->tags != NULL) {
|
||||
n->tags = (unsigned char *)malloc(n->size);
|
||||
if (n->tags == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
n->size);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
// n->size);
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_mem(): out of memory");
|
||||
}
|
||||
memcpy(n->tags, m->tags, n->size);
|
||||
}
|
||||
@@ -441,8 +446,9 @@ AVRPART * avr_new_part(void)
|
||||
|
||||
p = (AVRPART *)malloc(sizeof(AVRPART));
|
||||
if (p == NULL) {
|
||||
avrdude_message(MSG_INFO, "new_part(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "new_part(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("new_part(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
@@ -1135,9 +1135,10 @@ static void buspirate_setup(struct programmer_t *pgm)
|
||||
{
|
||||
/* Allocate private data */
|
||||
if ((pgm->cookie = calloc(1, sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("buspirate_initpgm(): Out of memory allocating private data\n");
|
||||
}
|
||||
PDATA(pgm)->serial_recv_timeout = 100;
|
||||
}
|
||||
|
||||
@@ -63,9 +63,10 @@ struct pdata
|
||||
static void butterfly_setup(PROGRAMMER * pgm)
|
||||
{
|
||||
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("butterfly_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(pgm->cookie, 0, sizeof(struct pdata));
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ static int fileio_num(struct fioparms * fio,
|
||||
char * filename, FILE * f, AVRMEM * mem, int size,
|
||||
FILEFMT fmt);
|
||||
|
||||
static int fmt_autodetect(char * fname, size_t offset);
|
||||
static int fmt_autodetect(char * fname, unsigned section);
|
||||
|
||||
|
||||
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
|
||||
{
|
||||
FILE *file;
|
||||
// On Windows we need to convert the filename to UTF-16
|
||||
@@ -118,16 +118,38 @@ static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offse
|
||||
file = fopen(filename, mode);
|
||||
#endif
|
||||
|
||||
if (file != NULL) {
|
||||
// Some systems allow seeking past the end of file, so we need check for that first and disallow
|
||||
if (fseek(file, 0, SEEK_END) != 0
|
||||
|| offset >= ftell(file)
|
||||
|| fseek(file, offset, SEEK_SET) != 0
|
||||
) {
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
errno = EINVAL;
|
||||
if (file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Seek to the specified 'section'
|
||||
static const char *hex_terminator = ":00000001FF\r";
|
||||
unsigned terms_seen = 0;
|
||||
char buffer[MAX_LINE_LEN + 1];
|
||||
|
||||
while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) {
|
||||
size_t len = strlen(buffer);
|
||||
|
||||
if (buffer[len - 1] == '\n') {
|
||||
len--;
|
||||
buffer[len] = 0;
|
||||
}
|
||||
if (buffer[len - 1] != '\r') {
|
||||
buffer[len] = '\r';
|
||||
len++;
|
||||
buffer[len] = 0;
|
||||
}
|
||||
|
||||
if (strcmp(buffer, hex_terminator) == 0) {
|
||||
// Found a section terminator
|
||||
terms_seen++;
|
||||
}
|
||||
}
|
||||
|
||||
if (feof(file)) {
|
||||
// Section not found
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
@@ -1392,7 +1414,7 @@ int fileio_setparms(int op, struct fioparms * fp,
|
||||
|
||||
|
||||
|
||||
static int fmt_autodetect(char * fname, size_t offset)
|
||||
static int fmt_autodetect(char * fname, unsigned section)
|
||||
{
|
||||
FILE * f;
|
||||
unsigned char buf[MAX_LINE_LEN];
|
||||
@@ -1402,9 +1424,9 @@ static int fmt_autodetect(char * fname, size_t offset)
|
||||
int first = 1;
|
||||
|
||||
#if defined(WIN32NATIVE)
|
||||
f = fopen_and_seek(fname, "r", offset);
|
||||
f = fopen_and_seek(fname, "r", section);
|
||||
#else
|
||||
f = fopen_and_seek(fname, "rb", offset);
|
||||
f = fopen_and_seek(fname, "rb", section);
|
||||
#endif
|
||||
|
||||
if (f == NULL) {
|
||||
@@ -1480,7 +1502,7 @@ static int fmt_autodetect(char * fname, size_t offset)
|
||||
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size, size_t offset)
|
||||
struct avrpart * p, char * memtype, int size, unsigned section)
|
||||
{
|
||||
int rc;
|
||||
FILE * f;
|
||||
@@ -1539,7 +1561,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
||||
return -1;
|
||||
}
|
||||
|
||||
format_detect = fmt_autodetect(fname, offset);
|
||||
format_detect = fmt_autodetect(fname, section);
|
||||
if (format_detect < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
|
||||
progname, fname);
|
||||
@@ -1570,7 +1592,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
||||
|
||||
if (format != FMT_IMM) {
|
||||
if (!using_stdio) {
|
||||
f = fopen_and_seek(fname, fio.mode, offset);
|
||||
f = fopen_and_seek(fname, fio.mode, section);
|
||||
if (f == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
|
||||
progname, fio.iodesc, fname, strerror(errno));
|
||||
|
||||
@@ -2834,7 +2834,8 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
||||
n = (yy_size_t) (_yybytes_len + 2);
|
||||
buf = (char *) yyalloc( n );
|
||||
if ( ! buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
|
||||
// YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
|
||||
avrdude_oom("out of dynamic memory in yy_scan_bytes()");
|
||||
|
||||
for ( i = 0; i < _yybytes_len; ++i )
|
||||
buf[i] = yybytes[i];
|
||||
@@ -2859,8 +2860,9 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
||||
|
||||
static void yynoreturn yy_fatal_error (const char* msg )
|
||||
{
|
||||
fprintf( stderr, "%s\n", msg );
|
||||
exit( YY_EXIT_FAILURE );
|
||||
fprintf( stderr, "%s\n", msg );
|
||||
// exit( YY_EXIT_FAILURE );
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Redefine yyless() so it works in section 3 code. */
|
||||
|
||||
@@ -821,7 +821,7 @@ extern "C" {
|
||||
char * fmtstr(FILEFMT format);
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size, size_t offset);
|
||||
struct avrpart * p, char * memtype, int size, unsigned section);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -870,7 +870,7 @@ enum updateflags {
|
||||
typedef struct update_t {
|
||||
char * memtype;
|
||||
int op;
|
||||
size_t offset;
|
||||
unsigned section;
|
||||
char * filename;
|
||||
int format;
|
||||
} UPDATE;
|
||||
@@ -882,7 +882,7 @@ extern "C" {
|
||||
extern UPDATE * parse_op(char * s);
|
||||
extern UPDATE * dup_update(UPDATE * upd);
|
||||
extern UPDATE * new_update(int op, char * memtype, int filefmt,
|
||||
char * filename, size_t offset);
|
||||
char * filename, unsigned section);
|
||||
extern void free_update(UPDATE * upd);
|
||||
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
|
||||
enum updateflags flags);
|
||||
|
||||
9
xs/src/avrdude/main-standalone.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "avrdude.h"
|
||||
|
||||
|
||||
static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return avrdude_main(argc, argv, SYS_CONFIG);
|
||||
}
|
||||
@@ -144,6 +144,33 @@ void avrdude_progress_external(const char *task, unsigned progress)
|
||||
avrdude_progress_handler(task, progress, avrdude_progress_handler_user_p);
|
||||
}
|
||||
|
||||
static void avrdude_oom_handler_null(const char *context, void *user_p)
|
||||
{
|
||||
// Output a message and just exit
|
||||
fputs("avrdude: Out of memory: ", stderr);
|
||||
fputs(context, stderr);
|
||||
exit(99);
|
||||
}
|
||||
|
||||
static void *avrdude_oom_handler_user_p = NULL;
|
||||
static avrdude_oom_handler_t avrdude_oom_handler = avrdude_oom_handler_null;
|
||||
|
||||
void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p)
|
||||
{
|
||||
if (newhandler != NULL) {
|
||||
avrdude_oom_handler = newhandler;
|
||||
avrdude_oom_handler_user_p = user_p;
|
||||
} else {
|
||||
avrdude_oom_handler = avrdude_oom_handler_null;
|
||||
avrdude_oom_handler_user_p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void avrdude_oom(const char *context)
|
||||
{
|
||||
avrdude_oom_handler(context, avrdude_oom_handler_user_p);
|
||||
}
|
||||
|
||||
void avrdude_cancel()
|
||||
{
|
||||
cancel_flag = true;
|
||||
@@ -194,7 +221,7 @@ static void usage(void)
|
||||
" -F Override invalid signature check.\n"
|
||||
" -e Perform a chip erase.\n"
|
||||
" -O Perform RC oscillator calibration (see AVR053). \n"
|
||||
" -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
|
||||
" -U <memtype>:r|w|v:<section>:<filename>[:format]\n"
|
||||
" Memory operation specification.\n"
|
||||
" Multiple -U options are allowed, each request\n"
|
||||
" is performed in the order specified.\n"
|
||||
|
||||
@@ -172,9 +172,10 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
|
||||
for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
|
||||
int *ip = malloc(sizeof(int));
|
||||
if (ip == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("out of memory allocating programmer structure\n");
|
||||
}
|
||||
*ip = *(int *) ldata(ln);
|
||||
ladd(pgm->usbpid, ip);
|
||||
|
||||
@@ -246,10 +246,11 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||
newname = malloc(strlen("\\\\.\\") + strlen(port) + 1);
|
||||
|
||||
if (newname == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
// avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("ser_open(): out of memory\n");
|
||||
}
|
||||
strcpy(newname, "\\\\.\\");
|
||||
strcat(newname, port);
|
||||
|
||||
@@ -311,8 +312,10 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||
static void ser_close(union filedescriptor *fd)
|
||||
{
|
||||
if (serial_over_ethernet) {
|
||||
#ifdef HAVE_LIBWS2_32
|
||||
closesocket(fd->ifd);
|
||||
WSACleanup();
|
||||
#endif
|
||||
} else {
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
if (hComPort != INVALID_HANDLE_VALUE)
|
||||
|
||||
@@ -295,9 +295,10 @@ static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p);
|
||||
void stk500v2_setup(PROGRAMMER * pgm)
|
||||
{
|
||||
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("stk500v2_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(pgm->cookie, 0, sizeof(struct pdata));
|
||||
PDATA(pgm)->command_sequence = 1;
|
||||
|
||||
@@ -38,8 +38,9 @@ UPDATE * parse_op(char * s)
|
||||
|
||||
upd = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (upd == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
|
||||
i = 0;
|
||||
@@ -53,8 +54,9 @@ UPDATE * parse_op(char * s)
|
||||
upd->op = DEVICE_WRITE;
|
||||
upd->filename = (char *)malloc(strlen(buf) + 1);
|
||||
if (upd->filename == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
strcpy(upd->filename, buf);
|
||||
upd->format = FMT_AUTO;
|
||||
@@ -63,8 +65,9 @@ UPDATE * parse_op(char * s)
|
||||
|
||||
upd->memtype = (char *)malloc(strlen(buf)+1);
|
||||
if (upd->memtype == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
strcpy(upd->memtype, buf);
|
||||
|
||||
@@ -101,22 +104,22 @@ UPDATE * parse_op(char * s)
|
||||
|
||||
p++;
|
||||
|
||||
// Extension: Parse file contents offset
|
||||
size_t offset = 0;
|
||||
// Extension: Parse file section number
|
||||
unsigned section = 0;
|
||||
|
||||
for (; *p != ':'; p++) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
offset *= 10;
|
||||
offset += *p - 0x30;
|
||||
section *= 10;
|
||||
section += *p - 0x30;
|
||||
} else {
|
||||
avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
|
||||
avrdude_message(MSG_INFO, "%s: invalid update specification: <section> is not a number\n", progname);
|
||||
free(upd->memtype);
|
||||
free(upd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
upd->offset = offset;
|
||||
upd->section = section;
|
||||
p++;
|
||||
|
||||
/*
|
||||
@@ -179,8 +182,9 @@ UPDATE * dup_update(UPDATE * upd)
|
||||
|
||||
u = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (u == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("dup_update: out of memory\n");
|
||||
}
|
||||
|
||||
memcpy(u, upd, sizeof(UPDATE));
|
||||
@@ -194,21 +198,22 @@ UPDATE * dup_update(UPDATE * upd)
|
||||
return u;
|
||||
}
|
||||
|
||||
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
|
||||
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsigned section)
|
||||
{
|
||||
UPDATE * u;
|
||||
|
||||
u = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (u == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("new_update: out of memory\n");
|
||||
}
|
||||
|
||||
u->memtype = strdup(memtype);
|
||||
u->filename = strdup(filename);
|
||||
u->op = op;
|
||||
u->format = filefmt;
|
||||
u->offset = offset;
|
||||
u->section = section;
|
||||
|
||||
return u;
|
||||
}
|
||||
@@ -286,7 +291,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
||||
progname,
|
||||
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
|
||||
}
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||
progname, upd->filename);
|
||||
@@ -351,7 +356,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
||||
progname, mem->desc, upd->filename);
|
||||
}
|
||||
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||
progname, upd->filename);
|
||||
|
||||
@@ -85,9 +85,10 @@ static void wiring_setup(PROGRAMMER * pgm)
|
||||
* Now prepare our data
|
||||
*/
|
||||
if ((mycookie = malloc(sizeof(struct wiringpdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("wiring_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(mycookie, 0, sizeof(struct wiringpdata));
|
||||
WIRINGPDATA(mycookie)->snoozetime = 0;
|
||||
|
||||
@@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(Libnest2D)
|
||||
|
||||
enable_testing()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
# Update if necessary
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ")
|
||||
@@ -32,6 +30,7 @@ set(LIBNEST2D_SRCFILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
|
||||
@@ -60,8 +59,7 @@ if(LIBNEST2D_GEOMETRIES_BACKEND STREQUAL "clipper")
|
||||
include_directories(BEFORE ${CLIPPER_INCLUDE_DIRS})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp
|
||||
list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/boost_alg.hpp)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${CLIPPER_LIBRARIES})
|
||||
list(APPEND LIBNEST2D_HEADERS ${CLIPPER_INCLUDE_DIRS}
|
||||
@@ -81,22 +79,12 @@ if(LIBNEST2D_OPTIMIZER_BACKEND STREQUAL "nlopt")
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/subplex.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/genetic.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/nlopt_boilerplate.hpp)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS}
|
||||
# Threads::Threads
|
||||
)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS})
|
||||
list(APPEND LIBNEST2D_HEADERS ${NLopt_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
# Currently we are outsourcing the non-convex NFP implementation from
|
||||
# libnfporb and it needs libgmp to work
|
||||
#find_package(GMP)
|
||||
#if(GMP_FOUND)
|
||||
# list(APPEND LIBNEST2D_LIBRARIES ${GMP_LIBRARIES})
|
||||
# list(APPEND LIBNEST2D_HEADERS ${GMP_INCLUDE_DIR})
|
||||
# add_definitions(-DLIBNFP_USE_RATIONAL)
|
||||
#endif()
|
||||
|
||||
if(LIBNEST2D_UNITTESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -27,5 +27,6 @@ set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
|
||||
|
||||
set(NLopt_LIBS nlopt)
|
||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR})
|
||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR}
|
||||
${nlopt_BINARY_DIR}/src/api)
|
||||
set(SHARED_LIBS_STATE ${SHARED_STATE})
|
||||
@@ -1,35 +0,0 @@
|
||||
# Try to find the GMP libraries:
|
||||
# GMP_FOUND - System has GMP lib
|
||||
# GMP_INCLUDE_DIR - The GMP include directory
|
||||
# GMP_LIBRARIES - Libraries needed to use GMP
|
||||
|
||||
if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
# Force search at every time, in case configuration changes
|
||||
unset(GMP_INCLUDE_DIR CACHE)
|
||||
unset(GMP_LIBRARIES CACHE)
|
||||
endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
|
||||
find_path(GMP_INCLUDE_DIR NAMES gmp.h)
|
||||
|
||||
if(WIN32)
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.a gmp gmp.lib mpir mpir.lib)
|
||||
else(WIN32)
|
||||
if(STBIN)
|
||||
message(STATUS "STBIN: ${STBIN}")
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.a gmp)
|
||||
else(STBIN)
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.so gmp)
|
||||
endif(STBIN)
|
||||
endif(WIN32)
|
||||
|
||||
if(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
set(GMP_FOUND TRUE)
|
||||
endif(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
|
||||
if(GMP_FOUND)
|
||||
message(STATUS "Configured GMP: ${GMP_LIBRARIES}")
|
||||
else(GMP_FOUND)
|
||||
message(STATUS "Could NOT find GMP")
|
||||
endif(GMP_FOUND)
|
||||
|
||||
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)
|
||||
@@ -535,19 +535,34 @@ void arrangeRectangles() {
|
||||
proba[0].rotate(Pi/3);
|
||||
proba[1].rotate(Pi-Pi/3);
|
||||
|
||||
// std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
|
||||
std::vector<Item> input;
|
||||
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
||||
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
||||
input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||
// input.insert(input.end(), rects.begin(), rects.end());
|
||||
input.insert(input.end(), proba.begin(), proba.end());
|
||||
// input.insert(input.end(), proba.begin(), proba.end());
|
||||
// input.insert(input.end(), crasher.begin(), crasher.end());
|
||||
|
||||
Box bin(250*SCALE, 210*SCALE);
|
||||
// PolygonImpl bin = {
|
||||
// {
|
||||
// {25*SCALE, 0},
|
||||
// {0, 25*SCALE},
|
||||
// {0, 225*SCALE},
|
||||
// {25*SCALE, 250*SCALE},
|
||||
// {225*SCALE, 250*SCALE},
|
||||
// {250*SCALE, 225*SCALE},
|
||||
// {250*SCALE, 25*SCALE},
|
||||
// {225*SCALE, 0},
|
||||
// {25*SCALE, 0}
|
||||
// },
|
||||
// {}
|
||||
// };
|
||||
|
||||
Coord min_obj_distance = 6*SCALE;
|
||||
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
||||
|
||||
using Placer = NfpPlacer;
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>;
|
||||
using Packer = Arranger<Placer, FirstFitSelection>;
|
||||
|
||||
Packer arrange(bin, min_obj_distance);
|
||||
@@ -556,28 +571,107 @@ void arrangeRectangles() {
|
||||
pconf.alignment = Placer::Config::Alignment::CENTER;
|
||||
pconf.starting_point = Placer::Config::Alignment::CENTER;
|
||||
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
|
||||
pconf.object_function = [&bin](Placer::Pile pile, double area,
|
||||
double norm, double penality) {
|
||||
pconf.accuracy = 0.5f;
|
||||
|
||||
auto bb = ShapeLike::boundingBox(pile);
|
||||
// auto bincenter = ShapeLike::boundingBox(bin).center();
|
||||
// pconf.object_function = [&bin, bincenter](
|
||||
// Placer::Pile pile, const Item& item,
|
||||
// double /*area*/, double norm, double penality) {
|
||||
|
||||
auto& sh = pile.back();
|
||||
auto rv = Nfp::referenceVertex(sh);
|
||||
auto c = bin.center();
|
||||
auto d = PointLike::distance(rv, c);
|
||||
double score = double(d)/norm;
|
||||
// using pl = PointLike;
|
||||
|
||||
// If it does not fit into the print bed we will beat it
|
||||
// with a large penality
|
||||
if(!NfpPlacer::wouldFit(bb, bin)) score = 2*penality - score;
|
||||
// static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
// static const double GRAVITY_RATIO = 0.5;
|
||||
// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
|
||||
|
||||
return score;
|
||||
};
|
||||
// // We will treat big items (compared to the print bed) differently
|
||||
// NfpPlacer::Pile bigs;
|
||||
// bigs.reserve(pile.size());
|
||||
// for(auto& p : pile) {
|
||||
// auto pbb = ShapeLike::boundingBox(p);
|
||||
// auto na = std::sqrt(pbb.width()*pbb.height())/norm;
|
||||
// if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p);
|
||||
// }
|
||||
|
||||
// // Candidate item bounding box
|
||||
// auto ibb = item.boundingBox();
|
||||
|
||||
// // Calculate the full bounding box of the pile with the candidate item
|
||||
// pile.emplace_back(item.transformedShape());
|
||||
// auto fullbb = ShapeLike::boundingBox(pile);
|
||||
// pile.pop_back();
|
||||
|
||||
// // The bounding box of the big items (they will accumulate in the center
|
||||
// // of the pile
|
||||
// auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs);
|
||||
|
||||
// // The size indicator of the candidate item. This is not the area,
|
||||
// // but almost...
|
||||
// auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm;
|
||||
|
||||
// // Will hold the resulting score
|
||||
// double score = 0;
|
||||
|
||||
// if(itemnormarea > BIG_ITEM_TRESHOLD) {
|
||||
// // This branch is for the bigger items..
|
||||
// // Here we will use the closest point of the item bounding box to
|
||||
// // the already arranged pile. So not the bb center nor the a choosen
|
||||
// // corner but whichever is the closest to the center. This will
|
||||
// // prevent unwanted strange arrangements.
|
||||
|
||||
// auto minc = ibb.minCorner(); // bottom left corner
|
||||
// auto maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// // top left and bottom right corners
|
||||
// auto top_left = PointImpl{getX(minc), getY(maxc)};
|
||||
// auto bottom_right = PointImpl{getX(maxc), getY(minc)};
|
||||
|
||||
// auto cc = fullbb.center(); // The gravity center
|
||||
|
||||
// // Now the distnce of the gravity center will be calculated to the
|
||||
// // five anchor points and the smallest will be chosen.
|
||||
// std::array<double, 5> dists;
|
||||
// dists[0] = pl::distance(minc, cc);
|
||||
// dists[1] = pl::distance(maxc, cc);
|
||||
// dists[2] = pl::distance(ibb.center(), cc);
|
||||
// dists[3] = pl::distance(top_left, cc);
|
||||
// dists[4] = pl::distance(bottom_right, cc);
|
||||
|
||||
// auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// // Density is the pack density: how big is the arranged pile
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
|
||||
// // The score is a weighted sum of the distance from pile center
|
||||
// // and the pile size
|
||||
// score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
|
||||
|
||||
// } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
|
||||
// // If there are no big items, only small, we should consider the
|
||||
// // density here as well to not get silly results
|
||||
// auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
// score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
|
||||
// } else {
|
||||
// // Here there are the small items that should be placed around the
|
||||
// // already processed bigger items.
|
||||
// // No need to play around with the anchor points, the center will be
|
||||
// // just fine for small items
|
||||
// score = pl::distance(ibb.center(), bigbb.center()) / norm;
|
||||
// }
|
||||
|
||||
// // If it does not fit into the print bed we will beat it
|
||||
// // with a large penality. If we would not do this, there would be only
|
||||
// // one big pile that doesn't care whether it fits onto the print bed.
|
||||
// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
|
||||
|
||||
// return score;
|
||||
// };
|
||||
|
||||
Packer::SelectionConfig sconf;
|
||||
// sconf.allow_parallel = false;
|
||||
// sconf.force_parallel = false;
|
||||
// sconf.try_triplets = false;
|
||||
// sconf.try_triplets = true;
|
||||
// sconf.try_reverse_order = true;
|
||||
// sconf.waste_increment = 0.005;
|
||||
|
||||
@@ -613,7 +707,7 @@ void arrangeRectangles() {
|
||||
std::vector<double> eff;
|
||||
eff.reserve(result.size());
|
||||
|
||||
auto bin_area = double(bin.height()*bin.width());
|
||||
auto bin_area = ShapeLike::area<PolygonImpl>(bin);
|
||||
for(auto& r : result) {
|
||||
double a = 0;
|
||||
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
|
||||
@@ -630,7 +724,7 @@ void arrangeRectangles() {
|
||||
<< " %" << std::endl;
|
||||
|
||||
std::cout << "Bin usage: (";
|
||||
unsigned total = 0;
|
||||
size_t total = 0;
|
||||
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
|
||||
std::cout << ") Total: " << total << std::endl;
|
||||
|
||||
@@ -643,10 +737,12 @@ void arrangeRectangles() {
|
||||
<< input.size() - total << " elements!"
|
||||
<< std::endl;
|
||||
|
||||
svg::SVGWriter::Config conf;
|
||||
using SVGWriter = svg::SVGWriter<PolygonImpl>;
|
||||
|
||||
SVGWriter::Config conf;
|
||||
conf.mm_in_coord_units = SCALE;
|
||||
svg::SVGWriter svgw(conf);
|
||||
svgw.setSize(bin);
|
||||
SVGWriter svgw(conf);
|
||||
svgw.setSize(Box(250*SCALE, 210*SCALE));
|
||||
svgw.writePackGroup(result);
|
||||
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
|
||||
svgw.save("out");
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <libnest2d/clipper_backend/clipper_backend.hpp>
|
||||
|
||||
// We include the stock optimizers for local and global optimization
|
||||
#include <libnest2d/optimizers/simplex.hpp> // Local subplex for NfpPlacer
|
||||
#include <libnest2d/optimizers/subplex.hpp> // Local subplex for NfpPlacer
|
||||
#include <libnest2d/optimizers/genetic.hpp> // Genetic for min. bounding box
|
||||
|
||||
#include <libnest2d/libnest2d.hpp>
|
||||
|
||||
@@ -8,8 +8,16 @@
|
||||
#ifdef __clang__
|
||||
#undef _MSC_EXTENSIONS
|
||||
#endif
|
||||
#include <boost/geometry.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
#include <boost/geometry.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
// this should be removed to not confuse the compiler
|
||||
// #include <libnest2d.h>
|
||||
|
||||
@@ -350,7 +358,7 @@ inline double ShapeLike::area(const PolygonImpl& shape)
|
||||
#endif
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::isInside(const PointImpl& point,
|
||||
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
|
||||
const PolygonImpl& shape)
|
||||
{
|
||||
return boost::geometry::within(point, shape);
|
||||
@@ -461,15 +469,6 @@ inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
||||
}
|
||||
#endif
|
||||
|
||||
//#ifndef DISABLE_BOOST_MINKOWSKI_ADD
|
||||
//template<>
|
||||
//inline PolygonImpl& Nfp::minkowskiAdd(PolygonImpl& sh,
|
||||
// const PolygonImpl& /*other*/)
|
||||
//{
|
||||
// return sh;
|
||||
//}
|
||||
//#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_SERIALIZE
|
||||
template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||
const PolygonImpl& sh, double scale)
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
//#include "clipper_backend.hpp"
|
||||
//#include <atomic>
|
||||
|
||||
//namespace libnest2d {
|
||||
|
||||
//namespace {
|
||||
|
||||
//class SpinLock {
|
||||
// std::atomic_flag& lck_;
|
||||
//public:
|
||||
|
||||
// inline SpinLock(std::atomic_flag& flg): lck_(flg) {}
|
||||
|
||||
// inline void lock() {
|
||||
// while(lck_.test_and_set(std::memory_order_acquire)) {}
|
||||
// }
|
||||
|
||||
// inline void unlock() { lck_.clear(std::memory_order_release); }
|
||||
//};
|
||||
|
||||
//class HoleCache {
|
||||
// friend struct libnest2d::ShapeLike;
|
||||
|
||||
// std::unordered_map< const PolygonImpl*, ClipperLib::Paths> map;
|
||||
|
||||
// ClipperLib::Paths& _getHoles(const PolygonImpl* p) {
|
||||
// static std::atomic_flag flg = ATOMIC_FLAG_INIT;
|
||||
// SpinLock lock(flg);
|
||||
|
||||
// lock.lock();
|
||||
// ClipperLib::Paths& paths = map[p];
|
||||
// lock.unlock();
|
||||
|
||||
// if(paths.size() != p->Childs.size()) {
|
||||
// paths.reserve(p->Childs.size());
|
||||
|
||||
// for(auto np : p->Childs) {
|
||||
// paths.emplace_back(np->Contour);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return paths;
|
||||
// }
|
||||
|
||||
// ClipperLib::Paths& getHoles(PolygonImpl& p) {
|
||||
// return _getHoles(&p);
|
||||
// }
|
||||
|
||||
// const ClipperLib::Paths& getHoles(const PolygonImpl& p) {
|
||||
// return _getHoles(&p);
|
||||
// }
|
||||
//};
|
||||
//}
|
||||
|
||||
//HoleCache holeCache;
|
||||
|
||||
//}
|
||||
|
||||
@@ -21,7 +21,7 @@ struct PolygonImpl {
|
||||
PathImpl Contour;
|
||||
HoleStore Holes;
|
||||
|
||||
inline PolygonImpl() {}
|
||||
inline PolygonImpl() = default;
|
||||
|
||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||
inline explicit PolygonImpl(const HoleStore& holes):
|
||||
@@ -66,6 +66,19 @@ inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) {
|
||||
ret -= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) {
|
||||
p.X *= pa.X;
|
||||
p.Y *= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) {
|
||||
PointImpl ret = p1;
|
||||
ret *= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace libnest2d {
|
||||
@@ -135,7 +148,7 @@ inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
|
||||
|
||||
namespace _smartarea {
|
||||
template<Orientation o>
|
||||
inline double area(const PolygonImpl& sh) {
|
||||
inline double area(const PolygonImpl& /*sh*/) {
|
||||
return std::nan("");
|
||||
}
|
||||
|
||||
@@ -220,22 +233,6 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
||||
}
|
||||
}
|
||||
|
||||
//template<> // TODO make it support holes if this method will ever be needed.
|
||||
//inline PolygonImpl Nfp::minkowskiDiff(const PolygonImpl& sh,
|
||||
// const PolygonImpl& other)
|
||||
//{
|
||||
// #define DISABLE_BOOST_MINKOWSKI_ADD
|
||||
|
||||
// ClipperLib::Paths solution;
|
||||
|
||||
// ClipperLib::MinkowskiDiff(sh.Contour, other.Contour, solution);
|
||||
|
||||
// PolygonImpl ret;
|
||||
// ret.Contour = solution.front();
|
||||
|
||||
// return sh;
|
||||
//}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
||||
std::stringstream ss;
|
||||
@@ -406,35 +403,12 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||
}
|
||||
|
||||
#define DISABLE_BOOST_NFP_MERGE
|
||||
template<> inline Nfp::Shapes<PolygonImpl>
|
||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
||||
{
|
||||
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
Nfp::Shapes<PolygonImpl> retv;
|
||||
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
bool closed = true;
|
||||
bool valid = false;
|
||||
|
||||
valid = clipper.AddPath(sh.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : sh.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
for(auto& path : shapes) {
|
||||
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : path.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid) throw GeometryException(GeomErr::MERGE);
|
||||
|
||||
ClipperLib::PolyTree result;
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
|
||||
retv.reserve(result.Total());
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||
retv.reserve(static_cast<size_t>(result.Total()));
|
||||
|
||||
std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
|
||||
|
||||
@@ -445,7 +419,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
||||
retv.push_back(poly);
|
||||
};
|
||||
|
||||
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly) {
|
||||
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
|
||||
{
|
||||
poly.Holes.push_back(pptr->Contour);
|
||||
poly.Holes.back().push_back(poly.Holes.back().front());
|
||||
for(auto c : pptr->Childs) processPoly(c);
|
||||
@@ -463,6 +438,27 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
||||
return retv;
|
||||
}
|
||||
|
||||
template<> inline Nfp::Shapes<PolygonImpl>
|
||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
||||
{
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
bool closed = true;
|
||||
bool valid = true;
|
||||
|
||||
for(auto& path : shapes) {
|
||||
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : path.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid) throw GeometryException(GeomErr::MERGE);
|
||||
|
||||
return _merge(clipper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//#define DISABLE_BOOST_SERIALIZE
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L
|
||||
#define BP2D_NOEXCEPT
|
||||
#define BP2D_CONSTEXPR
|
||||
#define BP2D_COMPILER_MSVC12
|
||||
#elif __cplusplus >= 201103L
|
||||
#define BP2D_NOEXCEPT noexcept
|
||||
#define BP2D_CONSTEXPR constexpr
|
||||
@@ -84,44 +85,6 @@ struct invoke_result {
|
||||
template<class F, class...Args>
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* C++14 std::index_sequence implementation: */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* \brief C++11 conformant implementation of the index_sequence type from C++14
|
||||
*/
|
||||
template<size_t...Ints> struct index_sequence {
|
||||
using value_type = size_t;
|
||||
BP2D_CONSTEXPR value_type size() const { return sizeof...(Ints); }
|
||||
};
|
||||
|
||||
// A Help structure to generate the integer list
|
||||
template<size_t...Nseq> struct genSeq;
|
||||
|
||||
// Recursive template to generate the list
|
||||
template<size_t I, size_t...Nseq> struct genSeq<I, Nseq...> {
|
||||
// Type will contain a genSeq with Nseq appended by one element
|
||||
using Type = typename genSeq< I - 1, I - 1, Nseq...>::Type;
|
||||
};
|
||||
|
||||
// Terminating recursion
|
||||
template <size_t ... Nseq> struct genSeq<0, Nseq...> {
|
||||
// If I is zero, Type will contain index_sequence with the fuly generated
|
||||
// integer list.
|
||||
using Type = index_sequence<Nseq...>;
|
||||
};
|
||||
|
||||
/// Helper alias to make an index sequence from 0 to N
|
||||
template<size_t N> using make_index_sequence = typename genSeq<N>::Type;
|
||||
|
||||
/// Helper alias to make an index sequence for a parameter pack
|
||||
template<class...Args>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Args)>;
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* A useful little tool for triggering static_assert error messages e.g. when
|
||||
* a mandatory template specialization (implementation) is missing.
|
||||
@@ -229,7 +192,7 @@ public:
|
||||
|
||||
GeomErr errcode() const { return errcode_; }
|
||||
|
||||
virtual const char * what() const BP2D_NOEXCEPT override {
|
||||
const char * what() const BP2D_NOEXCEPT override {
|
||||
return errorstr(errcode_).c_str();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
@@ -68,7 +69,7 @@ class _Box: PointPair<RawPoint> {
|
||||
using PointPair<RawPoint>::p2;
|
||||
public:
|
||||
|
||||
inline _Box() {}
|
||||
inline _Box() = default;
|
||||
inline _Box(const RawPoint& p, const RawPoint& pp):
|
||||
PointPair<RawPoint>({p, pp}) {}
|
||||
|
||||
@@ -85,6 +86,31 @@ public:
|
||||
inline TCoord<RawPoint> height() const BP2D_NOEXCEPT;
|
||||
|
||||
inline RawPoint center() const BP2D_NOEXCEPT;
|
||||
|
||||
inline double area() const BP2D_NOEXCEPT {
|
||||
return double(width()*height());
|
||||
}
|
||||
};
|
||||
|
||||
template<class RawPoint>
|
||||
class _Circle {
|
||||
RawPoint center_;
|
||||
double radius_ = 0;
|
||||
public:
|
||||
|
||||
_Circle() = default;
|
||||
|
||||
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
||||
|
||||
inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; }
|
||||
inline const void center(const RawPoint& c) { center_ = c; }
|
||||
|
||||
inline double radius() const BP2D_NOEXCEPT { return radius_; }
|
||||
inline void radius(double r) { radius_ = r; }
|
||||
|
||||
inline double area() const BP2D_NOEXCEPT {
|
||||
return 2.0*Pi*radius_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -97,7 +123,7 @@ class _Segment: PointPair<RawPoint> {
|
||||
mutable Radians angletox_ = std::nan("");
|
||||
public:
|
||||
|
||||
inline _Segment() {}
|
||||
inline _Segment() = default;
|
||||
|
||||
inline _Segment(const RawPoint& p, const RawPoint& pp):
|
||||
PointPair<RawPoint>({p, pp}) {}
|
||||
@@ -188,7 +214,7 @@ struct PointLike {
|
||||
|
||||
if( (y < y1 && y < y2) || (y > y1 && y > y2) )
|
||||
return {0, false};
|
||||
else if ((y == y1 && y == y2) && (x > x1 && x > x2))
|
||||
if ((y == y1 && y == y2) && (x > x1 && x > x2))
|
||||
ret = std::min( x-x1, x -x2);
|
||||
else if( (y == y1 && y == y2) && (x < x1 && x < x2))
|
||||
ret = -std::min(x1 - x, x2 - x);
|
||||
@@ -214,7 +240,7 @@ struct PointLike {
|
||||
|
||||
if( (x < x1 && x < x2) || (x > x1 && x > x2) )
|
||||
return {0, false};
|
||||
else if ((x == x1 && x == x2) && (y > y1 && y > y2))
|
||||
if ((x == x1 && x == x2) && (y > y1 && y > y2))
|
||||
ret = std::min( y-y1, y -y2);
|
||||
else if( (x == x1 && x == x2) && (y < y1 && y < y2))
|
||||
ret = -std::min(y1 - y, y2 - y);
|
||||
@@ -329,7 +355,7 @@ enum class Formats {
|
||||
};
|
||||
|
||||
// This struct serves as a namespace. The only difference is that it can be
|
||||
// used in friend declarations.
|
||||
// used in friend declarations and can be aliased at class scope.
|
||||
struct ShapeLike {
|
||||
|
||||
template<class RawShape>
|
||||
@@ -361,6 +387,51 @@ struct ShapeLike {
|
||||
return create<RawShape>(contour, {});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static size_t holeCount(const RawShape& sh)
|
||||
{
|
||||
return holes(sh).size();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getContour(RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
// Optional, does nothing by default
|
||||
template<class RawShape>
|
||||
static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
||||
@@ -402,7 +473,7 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<Formats, class RawShape>
|
||||
static std::string serialize(const RawShape& /*sh*/, double scale=1)
|
||||
static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::serialize() unimplemented!");
|
||||
@@ -498,51 +569,6 @@ struct ShapeLike {
|
||||
return RawShape();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static size_t holeCount(const RawShape& sh)
|
||||
{
|
||||
return holes(sh).size();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getContour(RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
||||
{
|
||||
@@ -614,6 +640,22 @@ struct ShapeLike {
|
||||
return box;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
TPoint<RawShape> pmin = {
|
||||
static_cast<Coord>(getX(circ.center()) - circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
|
||||
|
||||
TPoint<RawShape> pmax = {
|
||||
static_cast<Coord>(getX(circ.center()) + circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
|
||||
|
||||
return {pmin, pmax};
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
@@ -621,14 +663,74 @@ struct ShapeLike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static double area(const Shapes<RawShape>& shapes)
|
||||
static inline double area(const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
double ret = 0;
|
||||
std::accumulate(shapes.first(), shapes.end(),
|
||||
[](const RawShape& a, const RawShape& b) {
|
||||
return area(a) + area(b);
|
||||
return circ.area();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const Shapes<RawShape>& shapes)
|
||||
{
|
||||
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
|
||||
[](double a, const RawShape& b) {
|
||||
return a += area(b);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return PointLike::distance(point, circ.center()) < circ.radius();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto px = getX(point);
|
||||
auto py = getY(point);
|
||||
auto minx = getX(box.minCorner());
|
||||
auto miny = getY(box.minCorner());
|
||||
auto maxx = getX(box.maxCorner());
|
||||
auto maxy = getY(box.maxCorner());
|
||||
|
||||
return px > minx && px < maxx && py > miny && py < maxy;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const RawShape& sh,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return std::all_of(cbegin(sh), cend(sh),
|
||||
[&circ](const TPoint<RawShape>& p){
|
||||
return isInside<RawShape>(p, circ);
|
||||
});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& box,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return isInside<RawShape>(box.minCorner(), circ) &&
|
||||
isInside<RawShape>(box.maxCorner(), circ);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto iminX = getX(ibb.minCorner());
|
||||
auto imaxX = getX(ibb.maxCorner());
|
||||
auto iminY = getY(ibb.minCorner());
|
||||
auto imaxY = getY(ibb.maxCorner());
|
||||
|
||||
auto minX = getX(box.minCorner());
|
||||
auto maxX = getX(box.maxCorner());
|
||||
auto minY = getY(box.minCorner());
|
||||
auto maxY = getY(box.maxCorner());
|
||||
|
||||
return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
|
||||
}
|
||||
|
||||
template<class RawShape> // Potential O(1) implementation may exist
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include "geometry_traits.hpp"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
@@ -23,64 +25,22 @@ struct Nfp {
|
||||
template<class RawShape>
|
||||
using Shapes = typename ShapeLike::Shapes<RawShape>;
|
||||
|
||||
/// Minkowski addition (not used yet)
|
||||
/**
|
||||
* Merge a bunch of polygons with the specified additional polygon.
|
||||
*
|
||||
* \tparam RawShape the Polygon data type.
|
||||
* \param shc The pile of polygons that will be unified with sh.
|
||||
* \param sh A single polygon to unify with shc.
|
||||
*
|
||||
* \return A set of polygons that is the union of the input polygons. Note that
|
||||
* mostly it will be a set containing only one big polygon but if the input
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>;
|
||||
//using Coord = TCoord<Vertex>;
|
||||
using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
using std::signbit;
|
||||
|
||||
// Copy the orbiter (controur only), we will have to work on it
|
||||
RawShape orbiter = sl::create(sl::getContour(cother));
|
||||
|
||||
// Make the orbiter reverse oriented
|
||||
for(auto &v : sl::getContour(orbiter)) v = -v;
|
||||
|
||||
// An egde with additional data for marking it
|
||||
struct MarkedEdge { Edge e; Radians turn_angle; bool is_turning_point; };
|
||||
|
||||
// Container for marked edges
|
||||
using EdgeList = std::vector<MarkedEdge>;
|
||||
|
||||
EdgeList A, B;
|
||||
|
||||
auto fillEdgeList = [](EdgeList& L, const RawShape& poly) {
|
||||
L.reserve(sl::contourVertexCount(poly));
|
||||
|
||||
auto it = sl::cbegin(poly);
|
||||
auto nextit = std::next(it);
|
||||
|
||||
L.emplace_back({Edge(*it, *nextit), 0, false});
|
||||
it++; nextit++;
|
||||
|
||||
while(nextit != sl::cend(poly)) {
|
||||
Edge e(*it, *nextit);
|
||||
auto& L_prev = L.back();
|
||||
auto phi = L_prev.e.angleToXaxis();
|
||||
auto phi_prev = e.angleToXaxis();
|
||||
auto turn_angle = phi-phi_prev;
|
||||
if(turn_angle > Pi) turn_angle -= 2*Pi;
|
||||
L.emplace_back({
|
||||
e,
|
||||
turn_angle,
|
||||
signbit(turn_angle) != signbit(L_prev.turn_angle)
|
||||
});
|
||||
it++; nextit++;
|
||||
}
|
||||
|
||||
L.front().turn_angle = L.front().e.angleToXaxis() -
|
||||
L.back().e.angleToXaxis();
|
||||
|
||||
if(L.front().turn_angle > Pi) L.front().turn_angle -= 2*Pi;
|
||||
};
|
||||
|
||||
fillEdgeList(A, sh);
|
||||
fillEdgeList(B, orbiter);
|
||||
|
||||
return sh;
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,10 +55,12 @@ static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc, const RawShape& sh)
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
auto m = merge(shc);
|
||||
m.push_back(sh);
|
||||
return merge(m);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,16 +101,20 @@ template<class RawShape>
|
||||
static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find min x and min y vertex
|
||||
// find max x and max y vertex
|
||||
auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
||||
_vsort<RawShape>);
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
||||
|
||||
/// Helper function to get the NFP
|
||||
template<NfpLevel nfptype, class RawShape>
|
||||
static RawShape noFitPolygon(const RawShape& sh, const RawShape& other)
|
||||
static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
NfpImpl<RawShape, nfptype> nfp;
|
||||
return nfp(sh, other);
|
||||
@@ -167,44 +133,46 @@ static RawShape noFitPolygon(const RawShape& sh, const RawShape& other)
|
||||
* \tparam RawShape the Polygon data type.
|
||||
* \param sh The stationary polygon
|
||||
* \param cother The orbiting polygon
|
||||
* \return Returns the NFP of the two input polygons which have to be strictly
|
||||
* convex. The resulting NFP is proven to be convex as well in this case.
|
||||
* \return Returns a pair of the NFP and its reference vertex of the two input
|
||||
* polygons which have to be strictly convex. The resulting NFP is proven to be
|
||||
* convex as well in this case.
|
||||
*
|
||||
*/
|
||||
template<class RawShape>
|
||||
static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
||||
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
|
||||
|
||||
RawShape other = cother;
|
||||
|
||||
// Make the other polygon counter-clockwise
|
||||
std::reverse(ShapeLike::begin(other), ShapeLike::end(other));
|
||||
using sl = ShapeLike;
|
||||
|
||||
RawShape rsh; // Final nfp placeholder
|
||||
Vertex top_nfp;
|
||||
std::vector<Edge> edgelist;
|
||||
|
||||
auto cap = ShapeLike::contourVertexCount(sh) +
|
||||
ShapeLike::contourVertexCount(other);
|
||||
auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other);
|
||||
|
||||
// Reserve the needed memory
|
||||
edgelist.reserve(cap);
|
||||
ShapeLike::reserve(rsh, static_cast<unsigned long>(cap));
|
||||
sl::reserve(rsh, static_cast<unsigned long>(cap));
|
||||
|
||||
{ // place all edges from sh into edgelist
|
||||
auto first = ShapeLike::cbegin(sh);
|
||||
auto next = first + 1;
|
||||
auto endit = ShapeLike::cend(sh);
|
||||
auto first = sl::cbegin(sh);
|
||||
auto next = std::next(first);
|
||||
|
||||
while(next != endit) edgelist.emplace_back(*(first++), *(next++));
|
||||
while(next != sl::cend(sh)) {
|
||||
edgelist.emplace_back(*(first), *(next));
|
||||
++first; ++next;
|
||||
}
|
||||
}
|
||||
|
||||
{ // place all edges from other into edgelist
|
||||
auto first = ShapeLike::cbegin(other);
|
||||
auto next = first + 1;
|
||||
auto endit = ShapeLike::cend(other);
|
||||
auto first = sl::cbegin(other);
|
||||
auto next = std::next(first);
|
||||
|
||||
while(next != endit) edgelist.emplace_back(*(first++), *(next++));
|
||||
while(next != sl::cend(other)) {
|
||||
edgelist.emplace_back(*(next), *(first));
|
||||
++first; ++next;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the edges by angle to X axis.
|
||||
@@ -215,10 +183,16 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
||||
});
|
||||
|
||||
// Add the two vertices from the first edge into the final polygon.
|
||||
ShapeLike::addVertex(rsh, edgelist.front().first());
|
||||
ShapeLike::addVertex(rsh, edgelist.front().second());
|
||||
sl::addVertex(rsh, edgelist.front().first());
|
||||
sl::addVertex(rsh, edgelist.front().second());
|
||||
|
||||
auto tmp = std::next(ShapeLike::begin(rsh));
|
||||
// Sorting function for the nfp reference vertex search
|
||||
auto& cmp = _vsort<RawShape>;
|
||||
|
||||
// the reference (rightmost top) vertex so far
|
||||
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
|
||||
|
||||
auto tmp = std::next(sl::begin(rsh));
|
||||
|
||||
// Construct final nfp by placing each edge to the end of the previous
|
||||
for(auto eit = std::next(edgelist.begin());
|
||||
@@ -226,56 +200,325 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
||||
++eit)
|
||||
{
|
||||
auto d = *tmp - eit->first();
|
||||
auto p = eit->second() + d;
|
||||
Vertex p = eit->second() + d;
|
||||
|
||||
ShapeLike::addVertex(rsh, p);
|
||||
sl::addVertex(rsh, p);
|
||||
|
||||
// Set the new reference vertex
|
||||
if(cmp(top_nfp, p)) top_nfp = p;
|
||||
|
||||
tmp = std::next(tmp);
|
||||
}
|
||||
|
||||
// Now we have an nfp somewhere in the dark. We need to get it
|
||||
// to the right position around the stationary shape.
|
||||
// This is done by choosing the leftmost lowest vertex of the
|
||||
// orbiting polygon to be touched with the rightmost upper
|
||||
// vertex of the stationary polygon. In this configuration, the
|
||||
// reference vertex of the orbiting polygon (which can be dragged around
|
||||
// the nfp) will be its rightmost upper vertex that coincides with the
|
||||
// rightmost upper vertex of the nfp. No proof provided other than Jonas
|
||||
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
||||
// ("No fit polygon problem" - section 2.1.9)
|
||||
return {rsh, top_nfp};
|
||||
}
|
||||
|
||||
// TODO: dont do this here. Cache the rmu and lmd in Item and get translate
|
||||
// the nfp after this call
|
||||
template<class RawShape>
|
||||
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||
const RawShape& cother)
|
||||
{
|
||||
|
||||
auto csh = sh; // Copy sh, we will sort the verices in the copy
|
||||
auto& cmp = _vsort<RawShape>;
|
||||
std::sort(ShapeLike::begin(csh), ShapeLike::end(csh), cmp);
|
||||
std::sort(ShapeLike::begin(other), ShapeLike::end(other), cmp);
|
||||
// Algorithms are from the original algorithm proposed in paper:
|
||||
// https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
|
||||
|
||||
// leftmost lower vertex of the stationary polygon
|
||||
auto& touch_sh = *(std::prev(ShapeLike::end(csh)));
|
||||
// rightmost upper vertex of the orbiting polygon
|
||||
auto& touch_other = *(ShapeLike::begin(other));
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 1: Obtaining the minkowski sum
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculate the difference and move the orbiter to the touch position.
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = *(std::prev(ShapeLike::end(other))) + dtouch;
|
||||
// I guess this is not a full minkowski sum of the two input polygons by
|
||||
// definition. This yields a subset that is compatible with the next 2
|
||||
// algorithms.
|
||||
|
||||
// Get the righmost upper vertex of the nfp and move it to the RMU of
|
||||
// the orbiter because they should coincide.
|
||||
auto&& top_nfp = rightmostUpVertex(rsh);
|
||||
auto dnfp = top_other - top_nfp;
|
||||
std::for_each(ShapeLike::begin(rsh), ShapeLike::end(rsh),
|
||||
[&dnfp](Vertex& v) { v+= dnfp; } );
|
||||
using Result = NfpResult<RawShape>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Coord = TCoord<Vertex>;
|
||||
using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
using std::signbit;
|
||||
using std::sort;
|
||||
using std::vector;
|
||||
using std::ref;
|
||||
using std::reference_wrapper;
|
||||
|
||||
return rsh;
|
||||
// TODO The original algorithms expects the stationary polygon in
|
||||
// counter clockwise and the orbiter in clockwise order.
|
||||
// So for preventing any further complication, I will make the input
|
||||
// the way it should be, than make my way around the orientations.
|
||||
|
||||
// Reverse the stationary contour to counter clockwise
|
||||
auto stcont = sl::getContour(cstationary);
|
||||
std::reverse(stcont.begin(), stcont.end());
|
||||
RawShape stationary;
|
||||
sl::getContour(stationary) = stcont;
|
||||
|
||||
// Reverse the orbiter contour to counter clockwise
|
||||
auto orbcont = sl::getContour(cother);
|
||||
|
||||
std::reverse(orbcont.begin(), orbcont.end());
|
||||
|
||||
// Copy the orbiter (contour only), we will have to work on it
|
||||
RawShape orbiter;
|
||||
sl::getContour(orbiter) = orbcont;
|
||||
|
||||
// Step 1: Make the orbiter reverse oriented
|
||||
for(auto &v : sl::getContour(orbiter)) v = -v;
|
||||
|
||||
// An egde with additional data for marking it
|
||||
struct MarkedEdge {
|
||||
Edge e; Radians turn_angle = 0; bool is_turning_point = false;
|
||||
MarkedEdge() = default;
|
||||
MarkedEdge(const Edge& ed, Radians ta, bool tp):
|
||||
e(ed), turn_angle(ta), is_turning_point(tp) {}
|
||||
};
|
||||
|
||||
// Container for marked edges
|
||||
using EdgeList = vector<MarkedEdge>;
|
||||
|
||||
EdgeList A, B;
|
||||
|
||||
// This is how an edge list is created from the polygons
|
||||
auto fillEdgeList = [](EdgeList& L, const RawShape& poly, int dir) {
|
||||
L.reserve(sl::contourVertexCount(poly));
|
||||
|
||||
auto it = sl::cbegin(poly);
|
||||
auto nextit = std::next(it);
|
||||
|
||||
double turn_angle = 0;
|
||||
bool is_turn_point = false;
|
||||
|
||||
while(nextit != sl::cend(poly)) {
|
||||
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
|
||||
it++; nextit++;
|
||||
}
|
||||
|
||||
auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
|
||||
auto phi = e1.angleToXaxis();
|
||||
auto phi_prev = e2.angleToXaxis();
|
||||
auto TwoPi = 2.0*Pi;
|
||||
if(phi > Pi) phi -= TwoPi;
|
||||
if(phi_prev > Pi) phi_prev -= TwoPi;
|
||||
auto turn_angle = phi-phi_prev;
|
||||
if(turn_angle > Pi) turn_angle -= TwoPi;
|
||||
return phi-phi_prev;
|
||||
};
|
||||
|
||||
if(dir > 0) {
|
||||
auto eit = L.begin();
|
||||
auto enext = std::next(eit);
|
||||
|
||||
eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
|
||||
|
||||
while(enext != L.end()) {
|
||||
enext->turn_angle = getTurnAngle( enext->e, eit->e);
|
||||
enext->is_turning_point =
|
||||
signbit(enext->turn_angle) != signbit(eit->turn_angle);
|
||||
++eit; ++enext;
|
||||
}
|
||||
|
||||
L.front().is_turning_point = signbit(L.front().turn_angle) !=
|
||||
signbit(L.back().turn_angle);
|
||||
} else {
|
||||
std::cout << L.size() << std::endl;
|
||||
|
||||
auto eit = L.rbegin();
|
||||
auto enext = std::next(eit);
|
||||
|
||||
eit->turn_angle = getTurnAngle(L.back().e, L.front().e);
|
||||
|
||||
while(enext != L.rend()) {
|
||||
enext->turn_angle = getTurnAngle(enext->e, eit->e);
|
||||
enext->is_turning_point =
|
||||
signbit(enext->turn_angle) != signbit(eit->turn_angle);
|
||||
std::cout << enext->is_turning_point << " " << enext->turn_angle << std::endl;
|
||||
|
||||
++eit; ++enext;
|
||||
}
|
||||
|
||||
L.back().is_turning_point = signbit(L.back().turn_angle) !=
|
||||
signbit(L.front().turn_angle);
|
||||
}
|
||||
};
|
||||
|
||||
// Step 2: Fill the edgelists
|
||||
fillEdgeList(A, stationary, 1);
|
||||
fillEdgeList(B, orbiter, -1);
|
||||
|
||||
// A reference to a marked edge that also knows its container
|
||||
struct MarkedEdgeRef {
|
||||
reference_wrapper<MarkedEdge> eref;
|
||||
reference_wrapper<vector<MarkedEdgeRef>> container;
|
||||
Coord dir = 1; // Direction modifier
|
||||
|
||||
inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
|
||||
inline const Edge& edge() const { return eref.get().e; }
|
||||
inline Edge& edge() { return eref.get().e; }
|
||||
inline bool isTurningPoint() const {
|
||||
return eref.get().is_turning_point;
|
||||
}
|
||||
inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
|
||||
return &(container.get()) == &cont;
|
||||
}
|
||||
inline bool eq(const MarkedEdgeRef& mr) {
|
||||
return &(eref.get()) == &(mr.eref.get());
|
||||
}
|
||||
|
||||
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||
reference_wrapper<vector<MarkedEdgeRef>> ec):
|
||||
eref(er), container(ec), dir(1) {}
|
||||
|
||||
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||
reference_wrapper<vector<MarkedEdgeRef>> ec,
|
||||
Coord d):
|
||||
eref(er), container(ec), dir(d) {}
|
||||
};
|
||||
|
||||
using EdgeRefList = vector<MarkedEdgeRef>;
|
||||
|
||||
// Comparing two marked edges
|
||||
auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
|
||||
return e1.angleX() < e2.angleX();
|
||||
};
|
||||
|
||||
EdgeRefList Aref, Bref; // We create containers for the references
|
||||
Aref.reserve(A.size()); Bref.reserve(B.size());
|
||||
|
||||
// Fill reference container for the stationary polygon
|
||||
std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
|
||||
Aref.emplace_back( ref(me), ref(Aref) );
|
||||
});
|
||||
|
||||
// Fill reference container for the orbiting polygon
|
||||
std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
|
||||
Bref.emplace_back( ref(me), ref(Bref) );
|
||||
});
|
||||
|
||||
struct EdgeGroup { typename EdgeRefList::const_iterator first, last; };
|
||||
|
||||
auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
|
||||
(const EdgeGroup& Q, const EdgeGroup& R, bool positive)
|
||||
{
|
||||
|
||||
// Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
|
||||
// Sort the containers of edge references and merge them.
|
||||
// Q could be sorted only once and be reused here but we would still
|
||||
// need to merge it with sorted(R).
|
||||
|
||||
EdgeRefList merged;
|
||||
EdgeRefList S, seq;
|
||||
merged.reserve((Q.last - Q.first) + (R.last - R.first));
|
||||
|
||||
merged.insert(merged.end(), Q.first, Q.last);
|
||||
merged.insert(merged.end(), R.first, R.last);
|
||||
sort(merged.begin(), merged.end(), sortfn);
|
||||
|
||||
// Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
|
||||
// we dont use i, instead, q is an iterator into Q. k would be an index
|
||||
// into the merged sequence but we use "it" as an iterator for that
|
||||
|
||||
// here we obtain references for the containers for later comparisons
|
||||
const auto& Rcont = R.first->container.get();
|
||||
const auto& Qcont = Q.first->container.get();
|
||||
|
||||
// Set the intial direction
|
||||
Coord dir = positive? 1 : -1;
|
||||
|
||||
// roughly i = 1 (so q = Q.first) and s1 = q1 so S[0] = q;
|
||||
auto q = Q.first;
|
||||
S.push_back(*q++);
|
||||
|
||||
// Roughly step 3
|
||||
while(q != Q.last) {
|
||||
auto it = merged.begin();
|
||||
while(it != merged.end() && !(it->eq(*(Q.first))) ) {
|
||||
if(it->isFrom(Rcont)) {
|
||||
auto s = *it;
|
||||
s.dir = dir;
|
||||
S.push_back(s);
|
||||
}
|
||||
if(it->eq(*q)) {
|
||||
S.push_back(*q);
|
||||
if(it->isTurningPoint()) dir = -dir;
|
||||
if(q != Q.first) it += dir;
|
||||
}
|
||||
else it += dir;
|
||||
}
|
||||
++q; // "Set i = i + 1"
|
||||
}
|
||||
|
||||
// Step 4:
|
||||
|
||||
// "Let starting edge r1 be in position si in sequence"
|
||||
// whaaat? I guess this means the following:
|
||||
S[0] = *R.first;
|
||||
auto it = S.begin();
|
||||
|
||||
// "Set j = 1, next = 2, direction = 1, seq1 = si"
|
||||
// we dont use j, seq is expanded dynamically.
|
||||
dir = 1; auto next = std::next(R.first);
|
||||
|
||||
// Step 5:
|
||||
// "If all si edges have been allocated to seqj" should mean that
|
||||
// we loop until seq has equal size with S
|
||||
while(seq.size() < S.size()) {
|
||||
++it; if(it == S.end()) it = S.begin();
|
||||
|
||||
if(it->isFrom(Qcont)) {
|
||||
seq.push_back(*it); // "If si is from Q, j = j + 1, seqj = si"
|
||||
|
||||
// "If si is a turning point in Q,
|
||||
// direction = - direction, next = next + direction"
|
||||
if(it->isTurningPoint()) { dir = -dir; next += dir; }
|
||||
}
|
||||
|
||||
if(it->eq(*next) && dir == next->dir) { // "If si = direction.rnext"
|
||||
// "j = j + 1, seqj = si, next = next + direction"
|
||||
seq.push_back(*it); next += dir;
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
};
|
||||
|
||||
EdgeGroup R{ Bref.begin(), Bref.begin() }, Q{ Aref.begin(), Aref.end() };
|
||||
auto it = Bref.begin();
|
||||
bool orientation = true;
|
||||
EdgeRefList seqlist;
|
||||
seqlist.reserve(3*(Aref.size() + Bref.size()));
|
||||
|
||||
while(it != Bref.end()) // This is step 3 and step 4 in one loop
|
||||
if(it->isTurningPoint()) {
|
||||
R = {R.last, it++};
|
||||
auto seq = mink(Q, R, orientation);
|
||||
|
||||
// TODO step 6 (should be 5 shouldn't it?): linking edges from A
|
||||
// I don't get this step
|
||||
|
||||
seqlist.insert(seqlist.end(), seq.begin(), seq.end());
|
||||
orientation = !orientation;
|
||||
} else ++it;
|
||||
|
||||
if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 2: breaking Minkowski sums into track line trips
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 3: finding the boundary of the NFP from track line trips
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
return Result(stationary, Vertex());
|
||||
}
|
||||
|
||||
// Specializable NFP implementation class. Specialize it if you have a faster
|
||||
// or better NFP implementation
|
||||
template<class RawShape, NfpLevel nfptype>
|
||||
struct NfpImpl {
|
||||
RawShape operator()(const RawShape& sh, const RawShape& other) {
|
||||
NfpResult<RawShape> operator()(const RawShape& sh, const RawShape& other)
|
||||
{
|
||||
static_assert(nfptype == NfpLevel::CONVEX_ONLY,
|
||||
"Nfp::noFitPolygon() unimplemented!");
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "geometry_traits.hpp"
|
||||
#include "optimizer.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
@@ -27,6 +28,7 @@ class _Item {
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Box = _Box<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
|
||||
// The original shape that gets encapsulated.
|
||||
RawShape sh_;
|
||||
@@ -51,11 +53,18 @@ class _Item {
|
||||
|
||||
enum class Convexity: char {
|
||||
UNCHECKED,
|
||||
TRUE,
|
||||
FALSE
|
||||
C_TRUE,
|
||||
C_FALSE
|
||||
};
|
||||
|
||||
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
||||
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
|
||||
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
|
||||
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
||||
mutable struct BBCache {
|
||||
Box bb; bool valid; Vertex tr;
|
||||
BBCache(): valid(false), tr(0, 0) {}
|
||||
} bb_cache_;
|
||||
|
||||
public:
|
||||
|
||||
@@ -104,15 +113,15 @@ public:
|
||||
* @param il The initializer list of vertices.
|
||||
*/
|
||||
inline _Item(const std::initializer_list< Vertex >& il):
|
||||
sh_(ShapeLike::create<RawShape>(il)) {}
|
||||
sh_(sl::create<RawShape>(il)) {}
|
||||
|
||||
inline _Item(const TContour<RawShape>& contour,
|
||||
const THolesContainer<RawShape>& holes = {}):
|
||||
sh_(ShapeLike::create<RawShape>(contour, holes)) {}
|
||||
sh_(sl::create<RawShape>(contour, holes)) {}
|
||||
|
||||
inline _Item(TContour<RawShape>&& contour,
|
||||
THolesContainer<RawShape>&& holes):
|
||||
sh_(ShapeLike::create<RawShape>(std::move(contour),
|
||||
sh_(sl::create<RawShape>(std::move(contour),
|
||||
std::move(holes))) {}
|
||||
|
||||
/**
|
||||
@@ -122,31 +131,31 @@ public:
|
||||
*/
|
||||
inline std::string toString() const
|
||||
{
|
||||
return ShapeLike::toString(sh_);
|
||||
return sl::toString(sh_);
|
||||
}
|
||||
|
||||
/// Iterator tho the first contour vertex in the polygon.
|
||||
inline Iterator begin() const
|
||||
{
|
||||
return ShapeLike::cbegin(sh_);
|
||||
return sl::cbegin(sh_);
|
||||
}
|
||||
|
||||
/// Alias to begin()
|
||||
inline Iterator cbegin() const
|
||||
{
|
||||
return ShapeLike::cbegin(sh_);
|
||||
return sl::cbegin(sh_);
|
||||
}
|
||||
|
||||
/// Iterator to the last contour vertex.
|
||||
inline Iterator end() const
|
||||
{
|
||||
return ShapeLike::cend(sh_);
|
||||
return sl::cend(sh_);
|
||||
}
|
||||
|
||||
/// Alias to end()
|
||||
inline Iterator cend() const
|
||||
{
|
||||
return ShapeLike::cend(sh_);
|
||||
return sl::cend(sh_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +170,7 @@ public:
|
||||
*/
|
||||
inline Vertex vertex(unsigned long idx) const
|
||||
{
|
||||
return ShapeLike::vertex(sh_, idx);
|
||||
return sl::vertex(sh_, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +185,7 @@ public:
|
||||
inline void setVertex(unsigned long idx, const Vertex& v )
|
||||
{
|
||||
invalidateCache();
|
||||
ShapeLike::vertex(sh_, idx) = v;
|
||||
sl::vertex(sh_, idx) = v;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +200,7 @@ public:
|
||||
double ret ;
|
||||
if(area_cache_valid_) ret = area_cache_;
|
||||
else {
|
||||
ret = ShapeLike::area(offsettedShape());
|
||||
ret = sl::area(offsettedShape());
|
||||
area_cache_ = ret;
|
||||
area_cache_valid_ = true;
|
||||
}
|
||||
@@ -203,17 +212,17 @@ public:
|
||||
|
||||
switch(convexity_) {
|
||||
case Convexity::UNCHECKED:
|
||||
ret = ShapeLike::isConvex<RawShape>(ShapeLike::getContour(transformedShape()));
|
||||
convexity_ = ret? Convexity::TRUE : Convexity::FALSE;
|
||||
ret = sl::isConvex<RawShape>(sl::getContour(transformedShape()));
|
||||
convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
|
||||
break;
|
||||
case Convexity::TRUE: ret = true; break;
|
||||
case Convexity::FALSE:;
|
||||
case Convexity::C_TRUE: ret = true; break;
|
||||
case Convexity::C_FALSE:;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool isHoleConvex(unsigned holeidx) const {
|
||||
inline bool isHoleConvex(unsigned /*holeidx*/) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -223,11 +232,11 @@ public:
|
||||
|
||||
/// The number of the outer ring vertices.
|
||||
inline size_t vertexCount() const {
|
||||
return ShapeLike::contourVertexCount(sh_);
|
||||
return sl::contourVertexCount(sh_);
|
||||
}
|
||||
|
||||
inline size_t holeCount() const {
|
||||
return ShapeLike::holeCount(sh_);
|
||||
return sl::holeCount(sh_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,36 +244,39 @@ public:
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
inline bool isPointInside(const Vertex& p)
|
||||
inline bool isPointInside(const Vertex& p) const
|
||||
{
|
||||
return ShapeLike::isInside(p, sh_);
|
||||
return sl::isInside(p, transformedShape());
|
||||
}
|
||||
|
||||
inline bool isInside(const _Item& sh) const
|
||||
{
|
||||
return ShapeLike::isInside(transformedShape(), sh.transformedShape());
|
||||
return sl::isInside(transformedShape(), sh.transformedShape());
|
||||
}
|
||||
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& box);
|
||||
inline bool isInside(const RawShape& sh) const
|
||||
{
|
||||
return sl::isInside(transformedShape(), sh);
|
||||
}
|
||||
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
|
||||
inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
|
||||
|
||||
inline void translate(const Vertex& d) BP2D_NOEXCEPT
|
||||
{
|
||||
translation_ += d; has_translation_ = true;
|
||||
tr_cache_valid_ = false;
|
||||
translation(translation() + d);
|
||||
}
|
||||
|
||||
inline void rotate(const Radians& rads) BP2D_NOEXCEPT
|
||||
{
|
||||
rotation_ += rads;
|
||||
has_rotation_ = true;
|
||||
tr_cache_valid_ = false;
|
||||
rotation(rotation() + rads);
|
||||
}
|
||||
|
||||
inline void addOffset(Coord distance) BP2D_NOEXCEPT
|
||||
{
|
||||
offset_distance_ = distance;
|
||||
has_offset_ = true;
|
||||
offset_cache_valid_ = false;
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
inline void removeOffset() BP2D_NOEXCEPT {
|
||||
@@ -286,6 +298,8 @@ public:
|
||||
{
|
||||
if(rotation_ != rot) {
|
||||
rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
|
||||
rmt_valid_ = false; lmb_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +307,7 @@ public:
|
||||
{
|
||||
if(translation_ != tr) {
|
||||
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,9 +316,10 @@ public:
|
||||
if(tr_cache_valid_) return tr_cache_;
|
||||
|
||||
RawShape cpy = offsettedShape();
|
||||
if(has_rotation_) ShapeLike::rotate(cpy, rotation_);
|
||||
if(has_translation_) ShapeLike::translate(cpy, translation_);
|
||||
if(has_rotation_) sl::rotate(cpy, rotation_);
|
||||
if(has_translation_) sl::translate(cpy, translation_);
|
||||
tr_cache_ = cpy; tr_cache_valid_ = true;
|
||||
rmt_valid_ = false; lmb_valid_ = false;
|
||||
|
||||
return tr_cache_;
|
||||
}
|
||||
@@ -321,23 +337,53 @@ public:
|
||||
inline void resetTransformation() BP2D_NOEXCEPT
|
||||
{
|
||||
has_translation_ = false; has_rotation_ = false; has_offset_ = false;
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
inline Box boundingBox() const {
|
||||
return ShapeLike::boundingBox(transformedShape());
|
||||
if(!bb_cache_.valid) {
|
||||
bb_cache_.bb = sl::boundingBox(transformedShape());
|
||||
bb_cache_.tr = {0, 0};
|
||||
bb_cache_.valid = true;
|
||||
}
|
||||
|
||||
auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr;
|
||||
return {bb.minCorner() + tr, bb.maxCorner() + tr};
|
||||
}
|
||||
|
||||
inline Vertex referenceVertex() const {
|
||||
return rightmostTopVertex();
|
||||
}
|
||||
|
||||
inline Vertex rightmostTopVertex() const {
|
||||
if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
|
||||
auto& tsh = transformedShape();
|
||||
rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||
rmt_valid_ = true;
|
||||
}
|
||||
return *rmt_;
|
||||
}
|
||||
|
||||
inline Vertex leftmostBottomVertex() const {
|
||||
if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
|
||||
auto& tsh = transformedShape();
|
||||
lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||
lmb_valid_ = true;
|
||||
}
|
||||
return *lmb_;
|
||||
}
|
||||
|
||||
//Static methods:
|
||||
|
||||
inline static bool intersects(const _Item& sh1, const _Item& sh2)
|
||||
{
|
||||
return ShapeLike::intersects(sh1.transformedShape(),
|
||||
return sl::intersects(sh1.transformedShape(),
|
||||
sh2.transformedShape());
|
||||
}
|
||||
|
||||
inline static bool touches(const _Item& sh1, const _Item& sh2)
|
||||
{
|
||||
return ShapeLike::touches(sh1.transformedShape(),
|
||||
return sl::touches(sh1.transformedShape(),
|
||||
sh2.transformedShape());
|
||||
}
|
||||
|
||||
@@ -346,12 +392,11 @@ private:
|
||||
inline const RawShape& offsettedShape() const {
|
||||
if(has_offset_ ) {
|
||||
if(offset_cache_valid_) return offset_cache_;
|
||||
else {
|
||||
offset_cache_ = sh_;
|
||||
ShapeLike::offset(offset_cache_, offset_distance_);
|
||||
offset_cache_valid_ = true;
|
||||
return offset_cache_;
|
||||
}
|
||||
|
||||
offset_cache_ = sh_;
|
||||
sl::offset(offset_cache_, offset_distance_);
|
||||
offset_cache_valid_ = true;
|
||||
return offset_cache_;
|
||||
}
|
||||
return sh_;
|
||||
}
|
||||
@@ -359,10 +404,23 @@ private:
|
||||
inline void invalidateCache() const BP2D_NOEXCEPT
|
||||
{
|
||||
tr_cache_valid_ = false;
|
||||
lmb_valid_ = false; rmt_valid_ = false;
|
||||
area_cache_valid_ = false;
|
||||
offset_cache_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
convexity_ = Convexity::UNCHECKED;
|
||||
}
|
||||
|
||||
static inline bool vsort(const Vertex& v1, const Vertex& v2)
|
||||
{
|
||||
Coord &&x1 = getX(v1), &&x2 = getX(v2);
|
||||
Coord &&y1 = getY(v1), &&y2 = getY(v2);
|
||||
auto diff = y1 - y2;
|
||||
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
||||
return x1 < x2;
|
||||
|
||||
return diff < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -370,7 +428,6 @@ private:
|
||||
*/
|
||||
template<class RawShape>
|
||||
class _Rectangle: public _Item<RawShape> {
|
||||
RawShape sh_;
|
||||
using _Item<RawShape>::vertex;
|
||||
using TO = Orientation;
|
||||
public:
|
||||
@@ -415,9 +472,13 @@ public:
|
||||
};
|
||||
|
||||
template<class RawShape>
|
||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) {
|
||||
_Rectangle<RawShape> rect(box.width(), box.height());
|
||||
return _Item<RawShape>::isInside(rect);
|
||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
||||
return ShapeLike::isInside<RawShape>(boundingBox(), box);
|
||||
}
|
||||
|
||||
template<class RawShape> inline bool
|
||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
||||
return ShapeLike::isInside<RawShape>(transformedShape(), circ);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -874,9 +935,8 @@ private:
|
||||
|
||||
Radians findBestRotation(Item& item) {
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.stoplimit = 0.01;
|
||||
stopcr.absolute_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
stopcr.type = opt::StopLimitType::RELATIVE;
|
||||
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
||||
|
||||
auto orig_rot = item.rotation();
|
||||
@@ -910,7 +970,6 @@ private:
|
||||
if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
|
||||
item.removeOffset();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
227
xs/src/libnest2d/libnest2d/metaloop.hpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#ifndef METALOOP_HPP
|
||||
#define METALOOP_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* C++14 std::index_sequence implementation: */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* \brief C++11 conformant implementation of the index_sequence type from C++14
|
||||
*/
|
||||
template<size_t...Ints> struct index_sequence {
|
||||
using value_type = size_t;
|
||||
BP2D_CONSTEXPR value_type size() const { return sizeof...(Ints); }
|
||||
};
|
||||
|
||||
// A Help structure to generate the integer list
|
||||
template<size_t...Nseq> struct genSeq;
|
||||
|
||||
// Recursive template to generate the list
|
||||
template<size_t I, size_t...Nseq> struct genSeq<I, Nseq...> {
|
||||
// Type will contain a genSeq with Nseq appended by one element
|
||||
using Type = typename genSeq< I - 1, I - 1, Nseq...>::Type;
|
||||
};
|
||||
|
||||
// Terminating recursion
|
||||
template <size_t ... Nseq> struct genSeq<0, Nseq...> {
|
||||
// If I is zero, Type will contain index_sequence with the fuly generated
|
||||
// integer list.
|
||||
using Type = index_sequence<Nseq...>;
|
||||
};
|
||||
|
||||
/// Helper alias to make an index sequence from 0 to N
|
||||
template<size_t N> using make_index_sequence = typename genSeq<N>::Type;
|
||||
|
||||
/// Helper alias to make an index sequence for a parameter pack
|
||||
template<class...Args>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Args)>;
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
namespace opt {
|
||||
|
||||
using std::forward;
|
||||
using std::tuple;
|
||||
using std::get;
|
||||
using std::tuple_element;
|
||||
|
||||
/**
|
||||
* @brief Helper class to be able to loop over a parameter pack's elements.
|
||||
*/
|
||||
class metaloop {
|
||||
|
||||
// The implementation is based on partial struct template specializations.
|
||||
// Basically we need a template type that is callable and takes an integer
|
||||
// non-type template parameter which can be used to implement recursive calls.
|
||||
//
|
||||
// C++11 will not allow the usage of a plain template function that is why we
|
||||
// use struct with overloaded call operator. At the same time C++11 prohibits
|
||||
// partial template specialization with a non type parameter such as int. We
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
/*
|
||||
* Helper class to implement in-place functors.
|
||||
*
|
||||
* We want to be able to use inline functors like a lambda to keep the code
|
||||
* as clear as possible.
|
||||
*/
|
||||
template<int N, class Fn> class MapFn {
|
||||
Fn&& fn_;
|
||||
public:
|
||||
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
// We provide the index as the first parameter and the pack (or tuple)
|
||||
// element as the second parameter to the functor.
|
||||
fn_(N, forward<T>(pack_element));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of the template loop trick.
|
||||
* We create a mechanism for looping over a parameter pack in compile time.
|
||||
* \tparam Idx is the loop index which will be decremented at each recursion.
|
||||
* \tparam Args The parameter pack that will be processed.
|
||||
*
|
||||
*/
|
||||
template <typename Idx, class...Args>
|
||||
class _MetaLoop {};
|
||||
|
||||
// Implementation for the first element of Args...
|
||||
template <class...Args>
|
||||
class _MetaLoop<Int<0>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int N = 0;
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run( Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (get<ARGNUM-N>(valtup));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the N-th element of Args...
|
||||
template <int N, class...Args>
|
||||
class _MetaLoop<Int<N>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run(Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (std::get<ARGNUM-N>(valtup));
|
||||
|
||||
// Recursive call to process the next element of Args
|
||||
_MetaLoop<Int<N-1>, Args...> ().run(forward<Tup>(valtup),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiation: We must instantiate the template with the last index because
|
||||
* the generalized version calls the decremented instantiations recursively.
|
||||
* Once the instantiation with the first index is called, the terminating
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief The final usable function template.
|
||||
*
|
||||
* This is similar to what varags was on C but in compile time C++11.
|
||||
* You can call:
|
||||
* apply(<the mapping function>, <arbitrary number of arguments of any type>);
|
||||
* For example:
|
||||
*
|
||||
* struct mapfunc {
|
||||
* template<class T> void operator()(int N, T&& element) {
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* apply(mapfunc(), 'a', 10, 151.545);
|
||||
*
|
||||
* C++14:
|
||||
* apply([](int N, auto&& element){
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }, 'a', 10, 151.545);
|
||||
*
|
||||
* This yields the output:
|
||||
* The value of the parameter 0: a
|
||||
* The value of the parameter 1: 10
|
||||
* The value of the parameter 2: 151.545
|
||||
*
|
||||
* As an addition, the function can be called with a tuple as the second
|
||||
* parameter holding the arguments instead of a parameter pack.
|
||||
*
|
||||
*/
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, Args&&...args) {
|
||||
MetaLoop<Args...>().run(tuple<Args&&...>(forward<Args>(args)...),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple rvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>&& tup) {
|
||||
MetaLoop<Args...>().run(std::move(tup), forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple lvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple const reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, const tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function with its arguments encapsualted in a tuple.
|
||||
*/
|
||||
template<class Fn, class Tup, std::size_t...Is>
|
||||
inline static auto
|
||||
callFunWithTuple(Fn&& fn, Tup&& tup, index_sequence<Is...>) ->
|
||||
decltype(fn(std::get<Is>(tup)...))
|
||||
{
|
||||
return fn(std::get<Is>(tup)...);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // METALOOP_HPP
|
||||
@@ -10,8 +10,7 @@ namespace libnest2d { namespace opt {
|
||||
|
||||
using std::forward;
|
||||
using std::tuple;
|
||||
using std::get;
|
||||
using std::tuple_element;
|
||||
using std::make_tuple;
|
||||
|
||||
/// A Type trait for upper and lower limit of a numeric type.
|
||||
template<class T, class B = void >
|
||||
@@ -51,176 +50,7 @@ inline Bound<T> bound(const T& min, const T& max) { return Bound<T>(min, max); }
|
||||
template<class...Args> using Input = tuple<Args...>;
|
||||
|
||||
template<class...Args>
|
||||
inline tuple<Args...> initvals(Args...args) { return std::make_tuple(args...); }
|
||||
|
||||
/**
|
||||
* @brief Helper class to be able to loop over a parameter pack's elements.
|
||||
*/
|
||||
class metaloop {
|
||||
// The implementation is based on partial struct template specializations.
|
||||
// Basically we need a template type that is callable and takes an integer
|
||||
// non-type template parameter which can be used to implement recursive calls.
|
||||
//
|
||||
// C++11 will not allow the usage of a plain template function that is why we
|
||||
// use struct with overloaded call operator. At the same time C++11 prohibits
|
||||
// partial template specialization with a non type parameter such as int. We
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
/*
|
||||
* Helper class to implement in-place functors.
|
||||
*
|
||||
* We want to be able to use inline functors like a lambda to keep the code
|
||||
* as clear as possible.
|
||||
*/
|
||||
template<int N, class Fn> class MapFn {
|
||||
Fn&& fn_;
|
||||
public:
|
||||
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
// We provide the index as the first parameter and the pack (or tuple)
|
||||
// element as the second parameter to the functor.
|
||||
fn_(N, forward<T>(pack_element));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of the template loop trick.
|
||||
* We create a mechanism for looping over a parameter pack in compile time.
|
||||
* \tparam Idx is the loop index which will be decremented at each recursion.
|
||||
* \tparam Args The parameter pack that will be processed.
|
||||
*
|
||||
*/
|
||||
template <typename Idx, class...Args>
|
||||
class _MetaLoop {};
|
||||
|
||||
// Implementation for the first element of Args...
|
||||
template <class...Args>
|
||||
class _MetaLoop<Int<0>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int N = 0;
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run( Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (get<ARGNUM-N>(valtup));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the N-th element of Args...
|
||||
template <int N, class...Args>
|
||||
class _MetaLoop<Int<N>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run(Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (std::get<ARGNUM-N>(valtup));
|
||||
|
||||
// Recursive call to process the next element of Args
|
||||
_MetaLoop<Int<N-1>, Args...> ().run(forward<Tup>(valtup),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiation: We must instantiate the template with the last index because
|
||||
* the generalized version calls the decremented instantiations recursively.
|
||||
* Once the instantiation with the first index is called, the terminating
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief The final usable function template.
|
||||
*
|
||||
* This is similar to what varags was on C but in compile time C++11.
|
||||
* You can call:
|
||||
* apply(<the mapping function>, <arbitrary number of arguments of any type>);
|
||||
* For example:
|
||||
*
|
||||
* struct mapfunc {
|
||||
* template<class T> void operator()(int N, T&& element) {
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* apply(mapfunc(), 'a', 10, 151.545);
|
||||
*
|
||||
* C++14:
|
||||
* apply([](int N, auto&& element){
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }, 'a', 10, 151.545);
|
||||
*
|
||||
* This yields the output:
|
||||
* The value of the parameter 0: a
|
||||
* The value of the parameter 1: 10
|
||||
* The value of the parameter 2: 151.545
|
||||
*
|
||||
* As an addition, the function can be called with a tuple as the second
|
||||
* parameter holding the arguments instead of a parameter pack.
|
||||
*
|
||||
*/
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, Args&&...args) {
|
||||
MetaLoop<Args...>().run(tuple<Args&&...>(forward<Args>(args)...),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple rvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>&& tup) {
|
||||
MetaLoop<Args...>().run(std::move(tup), forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple lvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple const reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, const tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function with its arguments encapsualted in a tuple.
|
||||
*/
|
||||
template<class Fn, class Tup, std::size_t...Is>
|
||||
inline static auto
|
||||
callFunWithTuple(Fn&& fn, Tup&& tup, index_sequence<Is...>) ->
|
||||
decltype(fn(std::get<Is>(tup)...))
|
||||
{
|
||||
return fn(std::get<Is>(tup)...);
|
||||
}
|
||||
|
||||
};
|
||||
inline tuple<Args...> initvals(Args...args) { return make_tuple(args...); }
|
||||
|
||||
/**
|
||||
* @brief Specific optimization methods for which a default optimizer
|
||||
@@ -257,29 +87,20 @@ enum ResultCodes {
|
||||
template<class...Args>
|
||||
struct Result {
|
||||
ResultCodes resultcode;
|
||||
std::tuple<Args...> optimum;
|
||||
tuple<Args...> optimum;
|
||||
double score;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The stop limit can be specified as the absolute error or as the
|
||||
* relative error, just like in nlopt.
|
||||
*/
|
||||
enum class StopLimitType {
|
||||
ABSOLUTE,
|
||||
RELATIVE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A type for specifying the stop criteria.
|
||||
*/
|
||||
struct StopCriteria {
|
||||
|
||||
/// Relative or absolute termination error
|
||||
StopLimitType type = StopLimitType::RELATIVE;
|
||||
/// If the absolute value difference between two scores.
|
||||
double absolute_score_difference = std::nan("");
|
||||
|
||||
/// The error value that is interpredted depending on the type property.
|
||||
double stoplimit = 0.0001;
|
||||
/// If the relative value difference between two scores.
|
||||
double relative_score_difference = std::nan("");
|
||||
|
||||
unsigned max_iterations = 0;
|
||||
};
|
||||
@@ -310,11 +131,11 @@ public:
|
||||
* \return Returns a Result<Args...> structure.
|
||||
* An example call would be:
|
||||
* auto result = opt.optimize_min(
|
||||
* [](std::tuple<double> x) // object function
|
||||
* [](tuple<double> x) // object function
|
||||
* {
|
||||
* return std::pow(std::get<0>(x), 2);
|
||||
* },
|
||||
* std::make_tuple(-0.5), // initial value
|
||||
* make_tuple(-0.5), // initial value
|
||||
* {-1.0, 1.0} // search space bounds
|
||||
* );
|
||||
*/
|
||||
@@ -390,10 +211,14 @@ public:
|
||||
static_assert(always_false<T>::value, "Optimizer unimplemented!");
|
||||
}
|
||||
|
||||
DummyOptimizer(const StopCriteria&) {
|
||||
static_assert(always_false<T>::value, "Optimizer unimplemented!");
|
||||
}
|
||||
|
||||
template<class Func, class...Args>
|
||||
Result<Args...> optimize(Func&& func,
|
||||
std::tuple<Args...> initvals,
|
||||
Bound<Args>... args)
|
||||
Result<Args...> optimize(Func&& /*func*/,
|
||||
tuple<Args...> /*initvals*/,
|
||||
Bound<Args>... /*args*/)
|
||||
{
|
||||
return Result<Args...>();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
#ifndef NLOPT_BOILERPLATE_HPP
|
||||
#define NLOPT_BOILERPLATE_HPP
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
#include <nlopt.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <libnest2d/optimizer.hpp>
|
||||
#include <cassert>
|
||||
#include "libnest2d/metaloop.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace libnest2d { namespace opt {
|
||||
|
||||
nlopt::algorithm method2nloptAlg(Method m) {
|
||||
inline nlopt::algorithm method2nloptAlg(Method m) {
|
||||
|
||||
switch(m) {
|
||||
case Method::L_SIMPLEX: return nlopt::LN_NELDERMEAD;
|
||||
@@ -87,7 +97,7 @@ protected:
|
||||
|
||||
template<class Fn, class...Args>
|
||||
static double optfunc(const std::vector<double>& params,
|
||||
std::vector<double>& grad,
|
||||
std::vector<double>& /*grad*/,
|
||||
void *data)
|
||||
{
|
||||
auto fnptr = static_cast<remove_ref_t<Fn>*>(data);
|
||||
@@ -132,12 +142,10 @@ protected:
|
||||
default: ;
|
||||
}
|
||||
|
||||
switch(this->stopcr_.type) {
|
||||
case StopLimitType::ABSOLUTE:
|
||||
opt_.set_ftol_abs(stopcr_.stoplimit); break;
|
||||
case StopLimitType::RELATIVE:
|
||||
opt_.set_ftol_rel(stopcr_.stoplimit); break;
|
||||
}
|
||||
auto abs_diff = stopcr_.absolute_score_difference;
|
||||
auto rel_diff = stopcr_.relative_score_difference;
|
||||
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
|
||||
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
|
||||
|
||||
if(this->stopcr_.max_iterations > 0)
|
||||
opt_.set_maxeval(this->stopcr_.max_iterations );
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#endif
|
||||
#include "placer_boilerplate.hpp"
|
||||
#include "../geometry_traits_nfp.hpp"
|
||||
#include "libnest2d/optimizer.hpp"
|
||||
#include <cassert>
|
||||
|
||||
#include "tools/svgtools.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
|
||||
@@ -20,15 +24,60 @@ struct NfpPConfig {
|
||||
TOP_RIGHT,
|
||||
};
|
||||
|
||||
/// Which angles to try out for better results
|
||||
/// Which angles to try out for better results.
|
||||
std::vector<Radians> rotations;
|
||||
|
||||
/// Where to align the resulting packed pile
|
||||
/// Where to align the resulting packed pile.
|
||||
Alignment alignment;
|
||||
|
||||
/// Where to start putting objects in the bin.
|
||||
Alignment starting_point;
|
||||
|
||||
std::function<double(const Nfp::Shapes<RawShape>&, double, double, double)>
|
||||
/**
|
||||
* @brief A function object representing the fitting function in the
|
||||
* placement optimization process. (Optional)
|
||||
*
|
||||
* This is the most versatile tool to configure the placer. The fitting
|
||||
* function is evaluated many times when a new item is being placed into the
|
||||
* bin. The output should be a rated score of the new item's position.
|
||||
*
|
||||
* This is not a mandatory option as there is a default fitting function
|
||||
* that will optimize for the best pack efficiency. With a custom fitting
|
||||
* function you can e.g. influence the shape of the arranged pile.
|
||||
*
|
||||
* \param shapes The first parameter is a container with all the placed
|
||||
* polygons excluding the current candidate. You can calculate a bounding
|
||||
* box or convex hull on this pile of polygons without the candidate item
|
||||
* or push back the candidate item into the container and then calculate
|
||||
* some features.
|
||||
*
|
||||
* \param item The second parameter is the candidate item.
|
||||
*
|
||||
* \param occupied_area The third parameter is the sum of areas of the
|
||||
* items in the first parameter so you don't have to iterate through them
|
||||
* if you only need their area.
|
||||
*
|
||||
* \param norm A norming factor for physical dimensions. E.g. if your score
|
||||
* is the distance between the item and the bin center, you should divide
|
||||
* that distance with the norming factor. If the score is an area than
|
||||
* divide it with the square of the norming factor. Imagine it as a unit of
|
||||
* distance.
|
||||
*
|
||||
* \param penality The fifth parameter is the amount of minimum penality if
|
||||
* the arranged pile would't fit into the bin. You can use the wouldFit()
|
||||
* function to check this. Note that the pile can be outside the bin's
|
||||
* boundaries while the placement algorithm is running. Your job is only to
|
||||
* check if the pile could be translated into a position in the bin where
|
||||
* all the items would be inside. For a box shaped bin you can use the
|
||||
* pile's bounding box to check whether it's width and height is small
|
||||
* enough. If the pile would not fit, you have to make sure that the
|
||||
* resulting score will be higher then the penality value. A good solution
|
||||
* would be to set score = 2*penality-score in case the pile wouldn't fit
|
||||
* into the bin.
|
||||
*
|
||||
*/
|
||||
std::function<double(Nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
||||
double, double, double)>
|
||||
object_function;
|
||||
|
||||
/**
|
||||
@@ -38,11 +87,30 @@ struct NfpPConfig {
|
||||
*/
|
||||
float accuracy = 1.0;
|
||||
|
||||
/**
|
||||
* @brief If you want to see items inside other item's holes, you have to
|
||||
* turn this switch on.
|
||||
*
|
||||
* This will only work if a suitable nfp implementation is provided.
|
||||
* The library has no such implementation right now.
|
||||
*/
|
||||
bool explore_holes = false;
|
||||
|
||||
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
|
||||
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
|
||||
};
|
||||
|
||||
// A class for getting a point on the circumference of the polygon (in log time)
|
||||
/**
|
||||
* A class for getting a point on the circumference of the polygon (in log time)
|
||||
*
|
||||
* This is a transformation of the provided polygon to be able to pinpoint
|
||||
* locations on the circumference. The optimizer will pass a floating point
|
||||
* value e.g. within <0,1> and we have to transform this value quickly into a
|
||||
* coordinate on the circumference. By definition 0 should yield the first
|
||||
* vertex and 1.0 would be the last (which should coincide with first).
|
||||
*
|
||||
* We also have to make this work for the holes of the captured polygon.
|
||||
*/
|
||||
template<class RawShape> class EdgeCache {
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Coord = TCoord<Vertex>;
|
||||
@@ -57,6 +125,8 @@ template<class RawShape> class EdgeCache {
|
||||
|
||||
std::vector<ContourCache> holes_;
|
||||
|
||||
double accuracy_ = 1.0;
|
||||
|
||||
void createCache(const RawShape& sh) {
|
||||
{ // For the contour
|
||||
auto first = ShapeLike::cbegin(sh);
|
||||
@@ -90,21 +160,44 @@ template<class RawShape> class EdgeCache {
|
||||
}
|
||||
}
|
||||
|
||||
size_t stride(const size_t N) const {
|
||||
using std::ceil;
|
||||
using std::round;
|
||||
using std::pow;
|
||||
|
||||
return static_cast<Coord>(
|
||||
std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0)))
|
||||
);
|
||||
}
|
||||
|
||||
void fetchCorners() const {
|
||||
if(!contour_.corners.empty()) return;
|
||||
|
||||
// TODO Accuracy
|
||||
contour_.corners = contour_.distances;
|
||||
for(auto& d : contour_.corners) d /= contour_.full_distance;
|
||||
const auto N = contour_.distances.size();
|
||||
const auto S = stride(N);
|
||||
|
||||
contour_.corners.reserve(N / S + 1);
|
||||
auto N_1 = N-1;
|
||||
contour_.corners.emplace_back(0.0);
|
||||
for(size_t i = 0; i < N_1; i += S) {
|
||||
contour_.corners.emplace_back(
|
||||
contour_.distances.at(i) / contour_.full_distance);
|
||||
}
|
||||
}
|
||||
|
||||
void fetchHoleCorners(unsigned hidx) const {
|
||||
auto& hc = holes_[hidx];
|
||||
if(!hc.corners.empty()) return;
|
||||
|
||||
// TODO Accuracy
|
||||
hc.corners = hc.distances;
|
||||
for(auto& d : hc.corners) d /= hc.full_distance;
|
||||
const auto N = hc.distances.size();
|
||||
const auto S = stride(N);
|
||||
auto N_1 = N-1;
|
||||
hc.corners.reserve(N / S + 1);
|
||||
hc.corners.emplace_back(0.0);
|
||||
for(size_t i = 0; i < N_1; i += S) {
|
||||
hc.corners.emplace_back(
|
||||
hc.distances.at(i) / hc.full_distance);
|
||||
}
|
||||
}
|
||||
|
||||
inline Vertex coords(const ContourCache& cache, double distance) const {
|
||||
@@ -150,6 +243,9 @@ public:
|
||||
createCache(sh);
|
||||
}
|
||||
|
||||
/// Resolution of returned corners. The stride is derived from this value.
|
||||
void accuracy(double a /* within <0.0, 1.0>*/) { accuracy_ = a; }
|
||||
|
||||
/**
|
||||
* @brief Get a point on the circumference of a polygon.
|
||||
* @param distance A relative distance from the starting point to the end.
|
||||
@@ -176,24 +272,64 @@ public:
|
||||
return holes_[hidx].full_distance;
|
||||
}
|
||||
|
||||
/// Get the normalized distance values for each vertex
|
||||
inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
|
||||
fetchCorners();
|
||||
return contour_.corners;
|
||||
}
|
||||
|
||||
/// corners for a specific hole
|
||||
inline const std::vector<double>&
|
||||
corners(unsigned holeidx) const BP2D_NOEXCEPT {
|
||||
fetchHoleCorners(holeidx);
|
||||
return holes_[holeidx].corners;
|
||||
}
|
||||
|
||||
inline unsigned holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
|
||||
/// The number of holes in the abstracted polygon
|
||||
inline size_t holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
|
||||
|
||||
};
|
||||
|
||||
template<NfpLevel lvl>
|
||||
struct Lvl { static const NfpLevel value = lvl; };
|
||||
|
||||
template<class RawShape>
|
||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
||||
const _Item<RawShape>& stationary,
|
||||
const _Item<RawShape>& orbiter)
|
||||
{
|
||||
// The provided nfp is somewhere in the dark. We need to get it
|
||||
// to the right position around the stationary shape.
|
||||
// This is done by choosing the leftmost lowest vertex of the
|
||||
// orbiting polygon to be touched with the rightmost upper
|
||||
// vertex of the stationary polygon. In this configuration, the
|
||||
// reference vertex of the orbiting polygon (which can be dragged around
|
||||
// the nfp) will be its rightmost upper vertex that coincides with the
|
||||
// rightmost upper vertex of the nfp. No proof provided other than Jonas
|
||||
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
||||
// ("No fit polygon problem" - section 2.1.9)
|
||||
|
||||
auto touch_sh = stationary.rightmostTopVertex();
|
||||
auto touch_other = orbiter.leftmostBottomVertex();
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||
auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point
|
||||
ShapeLike::translate(nfp.first, dnfp);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
||||
const RawShape& stationary,
|
||||
const _Item<RawShape>& orbiter)
|
||||
{
|
||||
auto touch_sh = Nfp::rightmostUpVertex(stationary);
|
||||
auto touch_other = orbiter.leftmostBottomVertex();
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||
auto dnfp = top_other - nfp.second;
|
||||
ShapeLike::translate(nfp.first, dnfp);
|
||||
}
|
||||
|
||||
template<class RawShape, class Container>
|
||||
Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||
const _Item<RawShape>& trsh,
|
||||
@@ -203,18 +339,35 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||
|
||||
Nfp::Shapes<RawShape> nfps;
|
||||
|
||||
//int pi = 0;
|
||||
for(Item& sh : polygons) {
|
||||
auto subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
#ifndef NDEBUG
|
||||
auto vv = ShapeLike::isValid(sh.transformedShape());
|
||||
assert(vv.first);
|
||||
|
||||
auto vnfp = ShapeLike::isValid(subnfp);
|
||||
auto vnfp = ShapeLike::isValid(subnfp_r.first);
|
||||
assert(vnfp.first);
|
||||
#endif
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp);
|
||||
correctNfpPosition(subnfp_r, sh, trsh);
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp_r.first);
|
||||
|
||||
// double SCALE = 1000000;
|
||||
// using SVGWriter = svg::SVGWriter<RawShape>;
|
||||
// SVGWriter::Config conf;
|
||||
// conf.mm_in_coord_units = SCALE;
|
||||
// SVGWriter svgw(conf);
|
||||
// Box bin(250*SCALE, 210*SCALE);
|
||||
// svgw.setSize(bin);
|
||||
// for(int i = 0; i <= pi; i++) svgw.writeItem(polygons[i]);
|
||||
// svgw.writeItem(trsh);
|
||||
//// svgw.writeItem(Item(subnfp_r.first));
|
||||
// for(auto& n : nfps) svgw.writeItem(Item(n));
|
||||
// svgw.save("nfpout");
|
||||
// pi++;
|
||||
}
|
||||
|
||||
return nfps;
|
||||
@@ -227,50 +380,73 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||
{
|
||||
using Item = _Item<RawShape>;
|
||||
|
||||
Nfp::Shapes<RawShape> nfps, stationary;
|
||||
Nfp::Shapes<RawShape> nfps;
|
||||
|
||||
auto& orb = trsh.transformedShape();
|
||||
bool orbconvex = trsh.isContourConvex();
|
||||
|
||||
for(Item& sh : polygons) {
|
||||
stationary = Nfp::merge(stationary, sh.transformedShape());
|
||||
}
|
||||
Nfp::NfpResult<RawShape> subnfp;
|
||||
auto& stat = sh.transformedShape();
|
||||
|
||||
std::cout << "pile size: " << stationary.size() << std::endl;
|
||||
for(RawShape& sh : stationary) {
|
||||
if(sh.isContourConvex() && orbconvex)
|
||||
subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
||||
else if(orbconvex)
|
||||
subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
||||
else
|
||||
subnfp = Nfp::noFitPolygon<Level::value>(stat, orb);
|
||||
|
||||
RawShape subnfp;
|
||||
// if(sh.isContourConvex() && trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
// sh.transformedShape(), trsh.transformedShape());
|
||||
// } else {
|
||||
subnfp = Nfp::noFitPolygon<Level::value>( sh/*.transformedShape()*/,
|
||||
trsh.transformedShape());
|
||||
// }
|
||||
correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
// #ifndef NDEBUG
|
||||
// auto vv = ShapeLike::isValid(sh.transformedShape());
|
||||
// assert(vv.first);
|
||||
|
||||
// auto vnfp = ShapeLike::isValid(subnfp);
|
||||
// assert(vnfp.first);
|
||||
// #endif
|
||||
|
||||
// auto vnfp = ShapeLike::isValid(subnfp);
|
||||
// if(!vnfp.first) {
|
||||
// std::cout << vnfp.second << std::endl;
|
||||
// std::cout << ShapeLike::toString(subnfp) << std::endl;
|
||||
// }
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp);
|
||||
nfps = Nfp::merge(nfps, subnfp.first);
|
||||
}
|
||||
|
||||
return nfps;
|
||||
|
||||
|
||||
// using Item = _Item<RawShape>;
|
||||
// using sl = ShapeLike;
|
||||
|
||||
// Nfp::Shapes<RawShape> nfps, stationary;
|
||||
|
||||
// for(Item& sh : polygons) {
|
||||
// stationary = Nfp::merge(stationary, sh.transformedShape());
|
||||
// }
|
||||
|
||||
// for(RawShape& sh : stationary) {
|
||||
|
||||
//// auto vv = sl::isValid(sh);
|
||||
//// std::cout << vv.second << std::endl;
|
||||
|
||||
|
||||
// Nfp::NfpResult<RawShape> subnfp;
|
||||
// bool shconvex = sl::isConvex<RawShape>(sl::getContour(sh));
|
||||
// if(shconvex && trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
// sh, trsh.transformedShape());
|
||||
// } else if(trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(
|
||||
// sh, trsh.transformedShape());
|
||||
// }
|
||||
// else {
|
||||
// subnfp = Nfp::noFitPolygon<Level::value>( sh,
|
||||
// trsh.transformedShape());
|
||||
// }
|
||||
|
||||
// correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
// nfps = Nfp::merge(nfps, subnfp.first);
|
||||
// }
|
||||
|
||||
// return nfps;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
||||
RawShape, _Box<TPoint<RawShape>>, NfpPConfig<RawShape>> {
|
||||
template<class RawShape, class TBin = _Box<TPoint<RawShape>>>
|
||||
class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
|
||||
RawShape, TBin, NfpPConfig<RawShape>> {
|
||||
|
||||
using Base = PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
||||
RawShape, _Box<TPoint<RawShape>>, NfpPConfig<RawShape>>;
|
||||
using Base = PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
|
||||
RawShape, TBin, NfpPConfig<RawShape>>;
|
||||
|
||||
DECLARE_PLACER(Base)
|
||||
|
||||
@@ -280,28 +456,45 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
||||
const double penality_;
|
||||
|
||||
using MaxNfpLevel = Nfp::MaxNfpLevel<RawShape>;
|
||||
using sl = ShapeLike;
|
||||
|
||||
public:
|
||||
|
||||
using Pile = const Nfp::Shapes<RawShape>&;
|
||||
using Pile = Nfp::Shapes<RawShape>;
|
||||
|
||||
inline explicit _NofitPolyPlacer(const BinType& bin):
|
||||
Base(bin),
|
||||
norm_(std::sqrt(ShapeLike::area<RawShape>(bin))),
|
||||
norm_(std::sqrt(sl::area<RawShape>(bin))),
|
||||
penality_(1e6*norm_) {}
|
||||
|
||||
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
|
||||
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
||||
|
||||
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
|
||||
_NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
#endif
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const RawShape& bin) {
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
auto d = bbin.center() - bb.center();
|
||||
_Rectangle<RawShape> rect(bb.width(), bb.height());
|
||||
rect.translate(bb.minCorner() + d);
|
||||
return sl::isInside<RawShape>(rect.transformedShape(), bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const RawShape& bin) {
|
||||
auto bbch = ShapeLike::boundingBox<RawShape>(chull);
|
||||
auto bbin = ShapeLike::boundingBox<RawShape>(bin);
|
||||
auto d = bbin.minCorner() - bbch.minCorner();
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
auto d = bbch.center() - bbin.center();
|
||||
auto chullcpy = chull;
|
||||
ShapeLike::translate(chullcpy, d);
|
||||
return ShapeLike::isInside<RawShape>(chullcpy, bbin);
|
||||
sl::translate(chullcpy, d);
|
||||
return sl::isInside<RawShape>(chullcpy, bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const Box& bin)
|
||||
{
|
||||
auto bbch = ShapeLike::boundingBox<RawShape>(chull);
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
return wouldFit(bbch, bin);
|
||||
}
|
||||
|
||||
@@ -310,6 +503,17 @@ public:
|
||||
return bb.width() <= bin.width() && bb.height() <= bin.height();
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const _Circle<Vertex>& bin)
|
||||
{
|
||||
return sl::isInside<RawShape>(bb, bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull,
|
||||
const _Circle<Vertex>& bin)
|
||||
{
|
||||
return sl::isInside<RawShape>(chull, bin);
|
||||
}
|
||||
|
||||
PackResult trypack(Item& item) {
|
||||
|
||||
PackResult ret;
|
||||
@@ -348,7 +552,10 @@ public:
|
||||
std::vector<EdgeCache<RawShape>> ecache;
|
||||
ecache.reserve(nfps.size());
|
||||
|
||||
for(auto& nfp : nfps ) ecache.emplace_back(nfp);
|
||||
for(auto& nfp : nfps ) {
|
||||
ecache.emplace_back(nfp);
|
||||
ecache.back().accuracy(config_.accuracy);
|
||||
}
|
||||
|
||||
struct Optimum {
|
||||
double relpos;
|
||||
@@ -363,7 +570,7 @@ public:
|
||||
auto getNfpPoint = [&ecache](const Optimum& opt)
|
||||
{
|
||||
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
|
||||
ecache[opt.nfpidx].coords(opt.nfpidx, opt.relpos);
|
||||
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
||||
};
|
||||
|
||||
Nfp::Shapes<RawShape> pile;
|
||||
@@ -374,17 +581,25 @@ public:
|
||||
pile_area += mitem.area();
|
||||
}
|
||||
|
||||
auto merged_pile = Nfp::merge(pile);
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
auto _objfunc = config_.object_function?
|
||||
config_.object_function :
|
||||
[this](const Nfp::Shapes<RawShape>& pile, double occupied_area,
|
||||
double /*norm*/, double penality)
|
||||
[this, &merged_pile](
|
||||
Nfp::Shapes<RawShape>& /*pile*/,
|
||||
const Item& item,
|
||||
double occupied_area,
|
||||
double norm,
|
||||
double /*penality*/)
|
||||
{
|
||||
auto ch = ShapeLike::convexHull(pile);
|
||||
merged_pile.emplace_back(item.transformedShape());
|
||||
auto ch = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
// The pack ratio -- how much is the convex hull occupied
|
||||
double pack_rate = occupied_area/ShapeLike::area(ch);
|
||||
double pack_rate = occupied_area/sl::area(ch);
|
||||
|
||||
// ratio of waste
|
||||
double waste = 1.0 - pack_rate;
|
||||
@@ -394,7 +609,7 @@ public:
|
||||
// (larger) values.
|
||||
auto score = std::sqrt(waste);
|
||||
|
||||
if(!wouldFit(ch, bin_)) score = 2*penality - score;
|
||||
if(!wouldFit(ch, bin_)) score += norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
@@ -406,23 +621,31 @@ public:
|
||||
d += startpos;
|
||||
item.translation(d);
|
||||
|
||||
pile.emplace_back(item.transformedShape());
|
||||
|
||||
double occupied_area = pile_area + item.area();
|
||||
|
||||
double score = _objfunc(pile, occupied_area,
|
||||
double score = _objfunc(pile, item, occupied_area,
|
||||
norm_, penality_);
|
||||
|
||||
pile.pop_back();
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
auto boundaryCheck = [&](const Optimum& o) {
|
||||
auto v = getNfpPoint(o);
|
||||
auto d = v - iv;
|
||||
d += startpos;
|
||||
item.translation(d);
|
||||
|
||||
merged_pile.emplace_back(item.transformedShape());
|
||||
auto chull = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
return wouldFit(chull, bin_);
|
||||
};
|
||||
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.max_iterations = 1000;
|
||||
stopcr.stoplimit = 0.001;
|
||||
stopcr.type = opt::StopLimitType::RELATIVE;
|
||||
opt::TOptimizer<opt::Method::L_SIMPLEX> solver(stopcr);
|
||||
stopcr.max_iterations = 100;
|
||||
stopcr.relative_score_difference = 1e-6;
|
||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
||||
|
||||
Optimum optimum(0, 0);
|
||||
double best_score = penality_;
|
||||
@@ -441,7 +664,7 @@ public:
|
||||
std::for_each(cache.corners().begin(),
|
||||
cache.corners().end(),
|
||||
[ch, &contour_ofn, &solver, &best_score,
|
||||
&optimum] (double pos)
|
||||
&optimum, &boundaryCheck] (double pos)
|
||||
{
|
||||
try {
|
||||
auto result = solver.optimize_min(contour_ofn,
|
||||
@@ -450,10 +673,11 @@ public:
|
||||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
best_score = result.score;
|
||||
optimum.relpos = std::get<0>(result.optimum);
|
||||
optimum.nfpidx = ch;
|
||||
optimum.hidx = -1;
|
||||
Optimum o(std::get<0>(result.optimum), ch, -1);
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
@@ -472,7 +696,7 @@ public:
|
||||
std::for_each(cache.corners(hidx).begin(),
|
||||
cache.corners(hidx).end(),
|
||||
[&hole_ofn, &solver, &best_score,
|
||||
&optimum, ch, hidx]
|
||||
&optimum, ch, hidx, &boundaryCheck]
|
||||
(double pos)
|
||||
{
|
||||
try {
|
||||
@@ -482,10 +706,12 @@ public:
|
||||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
best_score = result.score;
|
||||
Optimum o(std::get<0>(result.optimum),
|
||||
ch, hidx);
|
||||
optimum = o;
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
@@ -524,34 +750,35 @@ public:
|
||||
m.reserve(items_.size());
|
||||
|
||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||
auto&& bb = ShapeLike::boundingBox<RawShape>(m);
|
||||
auto&& bb = sl::boundingBox<RawShape>(m);
|
||||
|
||||
Vertex ci, cb;
|
||||
auto bbin = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
switch(config_.alignment) {
|
||||
case Config::Alignment::CENTER: {
|
||||
ci = bb.center();
|
||||
cb = bin_.center();
|
||||
cb = bbin.center();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_LEFT: {
|
||||
ci = bb.minCorner();
|
||||
cb = bin_.minCorner();
|
||||
cb = bbin.minCorner();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_RIGHT: {
|
||||
ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
|
||||
cb = {getX(bin_.maxCorner()), getY(bin_.minCorner())};
|
||||
cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_LEFT: {
|
||||
ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
|
||||
cb = {getX(bin_.minCorner()), getY(bin_.maxCorner())};
|
||||
cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_RIGHT: {
|
||||
ci = bb.maxCorner();
|
||||
cb = bin_.maxCorner();
|
||||
cb = bbin.maxCorner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -567,31 +794,32 @@ private:
|
||||
void setInitialPosition(Item& item) {
|
||||
Box&& bb = item.boundingBox();
|
||||
Vertex ci, cb;
|
||||
auto bbin = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
switch(config_.starting_point) {
|
||||
case Config::Alignment::CENTER: {
|
||||
ci = bb.center();
|
||||
cb = bin_.center();
|
||||
cb = bbin.center();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_LEFT: {
|
||||
ci = bb.minCorner();
|
||||
cb = bin_.minCorner();
|
||||
cb = bbin.minCorner();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_RIGHT: {
|
||||
ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
|
||||
cb = {getX(bin_.maxCorner()), getY(bin_.minCorner())};
|
||||
cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_LEFT: {
|
||||
ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
|
||||
cb = {getX(bin_.minCorner()), getY(bin_.maxCorner())};
|
||||
cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_RIGHT: {
|
||||
ci = bb.maxCorner();
|
||||
cb = bin_.maxCorner();
|
||||
cb = bbin.maxCorner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -602,7 +830,7 @@ private:
|
||||
|
||||
void placeOutsideOfBin(Item& item) {
|
||||
auto&& bb = item.boundingBox();
|
||||
Box binbb = ShapeLike::boundingBox<RawShape>(bin_);
|
||||
Box binbb = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
|
||||
|
||||
|
||||
@@ -256,14 +256,14 @@ public:
|
||||
|
||||
if(not_packed.size() < 2)
|
||||
return false; // No group of two items
|
||||
else {
|
||||
double largest_area = not_packed.front().get().area();
|
||||
auto itmp = not_packed.begin(); itmp++;
|
||||
double second_largest = itmp->get().area();
|
||||
if( free_area - second_largest - largest_area > waste)
|
||||
return false; // If even the largest two items do not fill
|
||||
// the bin to the desired waste than we can end here.
|
||||
}
|
||||
|
||||
double largest_area = not_packed.front().get().area();
|
||||
auto itmp = not_packed.begin(); itmp++;
|
||||
double second_largest = itmp->get().area();
|
||||
if( free_area - second_largest - largest_area > waste)
|
||||
return false; // If even the largest two items do not fill
|
||||
// the bin to the desired waste than we can end here.
|
||||
|
||||
|
||||
bool ret = false;
|
||||
auto it = not_packed.begin();
|
||||
@@ -481,7 +481,7 @@ public:
|
||||
{
|
||||
std::array<bool, 3> packed = {false};
|
||||
|
||||
for(auto id : idx) packed[id] =
|
||||
for(auto id : idx) packed.at(id) =
|
||||
placer.pack(candidates[id]);
|
||||
|
||||
bool check =
|
||||
@@ -535,10 +535,9 @@ public:
|
||||
// then it should be removed from the not_packed list
|
||||
{ auto it = store_.begin();
|
||||
while (it != store_.end()) {
|
||||
Placer p(bin);
|
||||
Placer p(bin); p.configure(pconfig);
|
||||
if(!p.pack(*it)) {
|
||||
auto itmp = it++;
|
||||
store_.erase(itmp);
|
||||
it = store_.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
}
|
||||
@@ -605,8 +604,7 @@ public:
|
||||
if(placer.pack(*it)) {
|
||||
filled_area += it->get().area();
|
||||
free_area = bin_area - filled_area;
|
||||
auto itmp = it++;
|
||||
not_packed.erase(itmp);
|
||||
it = not_packed.erase(it);
|
||||
makeProgress(placer, idx, 1);
|
||||
} else it++;
|
||||
}
|
||||
|
||||
@@ -52,17 +52,16 @@ public:
|
||||
auto total = last-first;
|
||||
auto makeProgress = [this, &total](Placer& placer, size_t idx) {
|
||||
packed_bins_[idx] = placer.getItems();
|
||||
this->progress_(--total);
|
||||
this->progress_(static_cast<unsigned>(--total));
|
||||
};
|
||||
|
||||
// Safety test: try to pack each item into an empty bin. If it fails
|
||||
// then it should be removed from the list
|
||||
{ auto it = store_.begin();
|
||||
while (it != store_.end()) {
|
||||
Placer p(bin);
|
||||
Placer p(bin); p.configure(pconfig);
|
||||
if(!p.pack(*it)) {
|
||||
auto itmp = it++;
|
||||
store_.erase(itmp);
|
||||
it = store_.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +682,9 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
||||
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
||||
orbiter.transformedShape());
|
||||
|
||||
auto v = ShapeLike::isValid(nfp);
|
||||
strategies::correctNfpPosition(nfp, stationary, orbiter);
|
||||
|
||||
auto v = ShapeLike::isValid(nfp.first);
|
||||
|
||||
if(!v.first) {
|
||||
std::cout << v.second << std::endl;
|
||||
@@ -690,7 +692,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
||||
|
||||
ASSERT_TRUE(v.first);
|
||||
|
||||
Item infp(nfp);
|
||||
Item infp(nfp.first);
|
||||
|
||||
int i = 0;
|
||||
auto rorbiter = orbiter.transformedShape();
|
||||
@@ -742,6 +744,15 @@ TEST(GeometryAlgorithms, nfpConvexConvex) {
|
||||
// testNfp<NfpLevel::BOTH_CONCAVE, 1000>(nfp_concave_testdata);
|
||||
//}
|
||||
|
||||
TEST(GeometryAlgorithms, nfpConcaveConcave) {
|
||||
using namespace libnest2d;
|
||||
|
||||
// Rectangle r1(10, 10);
|
||||
// Rectangle r2(20, 20);
|
||||
// auto result = Nfp::nfpSimpleSimple(r1.transformedShape(),
|
||||
// r2.transformedShape());
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, pointOnPolygonContour) {
|
||||
using namespace libnest2d;
|
||||
|
||||
|
||||
@@ -49,18 +49,18 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
|
||||
long double px = p.x_.val();
|
||||
long double py = p.y_.val();
|
||||
#endif
|
||||
return libnfporb::point_t(px*factor, py*factor);
|
||||
return {px*factor, py*factor};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
{
|
||||
using Vertex = PointImpl;
|
||||
|
||||
PolygonImpl ret;
|
||||
NfpR ret;
|
||||
|
||||
// try {
|
||||
try {
|
||||
libnfporb::polygon_t pstat, porb;
|
||||
|
||||
boost::geometry::convert(sh, pstat);
|
||||
@@ -85,7 +85,7 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
// this can throw
|
||||
auto nfp = libnfporb::generateNFP(pstat, porb, true);
|
||||
|
||||
auto &ct = ShapeLike::getContour(ret);
|
||||
auto &ct = ShapeLike::getContour(ret.first);
|
||||
ct.reserve(nfp.front().size()+1);
|
||||
for(auto v : nfp.front()) {
|
||||
v = scale(v, refactor);
|
||||
@@ -94,10 +94,10 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
ct.push_back(ct.front());
|
||||
std::reverse(ct.begin(), ct.end());
|
||||
|
||||
auto &rholes = ShapeLike::holes(ret);
|
||||
auto &rholes = ShapeLike::holes(ret.first);
|
||||
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
|
||||
if(nfp[hidx].size() >= 3) {
|
||||
rholes.push_back({});
|
||||
rholes.emplace_back();
|
||||
auto& h = rholes.back();
|
||||
h.reserve(nfp[hidx].size()+1);
|
||||
|
||||
@@ -110,73 +110,48 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
}
|
||||
}
|
||||
|
||||
auto& cmp = vsort;
|
||||
std::sort(pstat.outer().begin(), pstat.outer().end(), cmp);
|
||||
std::sort(porb.outer().begin(), porb.outer().end(), cmp);
|
||||
ret.second = Nfp::referenceVertex(ret.first);
|
||||
|
||||
// leftmost lower vertex of the stationary polygon
|
||||
auto& touch_sh = scale(pstat.outer().back(), refactor);
|
||||
// rightmost upper vertex of the orbiting polygon
|
||||
auto& touch_other = scale(porb.outer().front(), refactor);
|
||||
|
||||
// Calculate the difference and move the orbiter to the touch position.
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto _top_other = scale(porb.outer().back(), refactor) + dtouch;
|
||||
|
||||
Vertex top_other(getX(_top_other), getY(_top_other));
|
||||
|
||||
// Get the righmost upper vertex of the nfp and move it to the RMU of
|
||||
// the orbiter because they should coincide.
|
||||
auto&& top_nfp = Nfp::rightmostUpVertex(ret);
|
||||
auto dnfp = top_other - top_nfp;
|
||||
|
||||
std::for_each(ShapeLike::begin(ret), ShapeLike::end(ret),
|
||||
[&dnfp](Vertex& v) { v+= dnfp; } );
|
||||
|
||||
for(auto& h : ShapeLike::holes(ret))
|
||||
std::for_each( h.begin(), h.end(),
|
||||
[&dnfp](Vertex& v) { v += dnfp; } );
|
||||
|
||||
// } catch(std::exception& e) {
|
||||
// std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
// auto ch_stat = ShapeLike::convexHull(sh);
|
||||
// auto ch_orb = ShapeLike::convexHull(cother);
|
||||
// ret = Nfp::nfpConvexOnly(ch_stat, ch_orb);
|
||||
// }
|
||||
ret = Nfp::nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl
|
||||
Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
//PolygonImpl
|
||||
//Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES>::operator()(
|
||||
// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
//{
|
||||
// return _nfp(sh, cother);
|
||||
//}
|
||||
|
||||
PolygonImpl
|
||||
Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
//PolygonImpl
|
||||
//Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES>::operator()(
|
||||
// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
//{
|
||||
// return _nfp(sh, cother);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,37 +5,39 @@
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
PolygonImpl _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
using NfpR = Nfp::NfpResult<PolygonImpl>;
|
||||
|
||||
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
//template<>
|
||||
//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
//template<>
|
||||
//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
|
||||
static const BP2D_CONSTEXPR NfpLevel value =
|
||||
// NfpLevel::CONVEX_ONLY;
|
||||
NfpLevel::BOTH_CONCAVE_WITH_HOLES;
|
||||
NfpLevel::BOTH_CONCAVE;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include <libnest2d.h>
|
||||
#include <libnest2d/libnest2d.hpp>
|
||||
|
||||
namespace libnest2d { namespace svg {
|
||||
|
||||
template<class RawShape>
|
||||
class SVGWriter {
|
||||
using Item = _Item<RawShape>;
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
using Box = _Box<TPoint<RawShape>>;
|
||||
using PackGroup = _PackGroup<RawShape>;
|
||||
|
||||
public:
|
||||
|
||||
enum OrigoLocation {
|
||||
|
||||
@@ -603,6 +603,8 @@ namespace Slic3r {
|
||||
|
||||
if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr))
|
||||
return false;
|
||||
|
||||
object.second->center_around_origin();
|
||||
}
|
||||
|
||||
// fixes the min z of the model if negative
|
||||
@@ -1491,6 +1493,7 @@ namespace Slic3r {
|
||||
|
||||
stl_get_size(&stl);
|
||||
volume->mesh.repair();
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// apply volume's name and config data
|
||||
for (const Metadata& metadata : volume_data.metadata)
|
||||
@@ -1989,7 +1992,7 @@ namespace Slic3r {
|
||||
|
||||
// stores object's name
|
||||
if (!obj->name.empty())
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
|
||||
|
||||
// stores object's config data
|
||||
for (const std::string& key : obj->config.keys())
|
||||
@@ -2012,7 +2015,7 @@ namespace Slic3r {
|
||||
|
||||
// stores volume's name
|
||||
if (!volume->name.empty())
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
|
||||
|
||||
// stores volume's modifier field
|
||||
if (volume->modifier)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../slic3r/GUI/PresetBundle.hpp"
|
||||
#include "AMF.hpp"
|
||||
|
||||
@@ -405,6 +406,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
break;
|
||||
@@ -686,33 +688,6 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string xml_escape(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
pos = text.find_first_of("\"\'&<>", pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string replacement;
|
||||
switch (text[pos])
|
||||
{
|
||||
case '\"': replacement = """; break;
|
||||
case '\'': replacement = "'"; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = "<"; break;
|
||||
case '>': replacement = ">"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text.replace(pos, 1, replacement);
|
||||
pos += replacement.size();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
|
||||
@@ -761,7 +736,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
||||
for (const std::string &key : object->config.keys())
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
|
||||
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
@@ -805,7 +780,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
||||
for (const std::string &key : volume->config.keys())
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
||||
if (!volume->name.empty())
|
||||
stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
|
||||
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
|
||||
if (volume->modifier)
|
||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||
|
||||
@@ -167,6 +167,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float alpha = m_wipe_tower_rotation/180.f * M_PI;
|
||||
WipeTower::xy start_pos = tcr.start_pos;
|
||||
WipeTower::xy end_pos = tcr.end_pos;
|
||||
start_pos.rotate(alpha);
|
||||
start_pos.translate(m_wipe_tower_pos);
|
||||
end_pos.rotate(alpha);
|
||||
end_pos.translate(m_wipe_tower_pos);
|
||||
std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
|
||||
|
||||
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += "M900 K0\n";
|
||||
// Move over the wipe tower.
|
||||
@@ -174,14 +186,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
||||
gcode += gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
|
||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += tcr.gcode;
|
||||
gcode += tcr_rotated_gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
|
||||
gcodegen.writer().toolchange(new_extruder_id);
|
||||
@@ -195,18 +207,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
if (new_extruder_id >= 0) {
|
||||
// Start the wipe at the current position.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
|
||||
tcr.end_pos.y)));
|
||||
WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
|
||||
end_pos.y)));
|
||||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
@@ -214,6 +226,57 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
|
||||
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
|
||||
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
|
||||
{
|
||||
std::istringstream gcode_str(gcode_original);
|
||||
std::string gcode_out;
|
||||
std::string line;
|
||||
WipeTower::xy pos = start_pos;
|
||||
WipeTower::xy transformed_pos;
|
||||
WipeTower::xy old_pos(-1000.1f, -1000.1f);
|
||||
|
||||
while (gcode_str) {
|
||||
std::getline(gcode_str, line); // we read the gcode line by line
|
||||
if (line.find("G1 ") == 0) {
|
||||
std::ostringstream line_out;
|
||||
std::istringstream line_str(line);
|
||||
line_str >> std::noskipws; // don't skip whitespace
|
||||
char ch = 0;
|
||||
while (line_str >> ch) {
|
||||
if (ch == 'X')
|
||||
line_str >> pos.x;
|
||||
else
|
||||
if (ch == 'Y')
|
||||
line_str >> pos.y;
|
||||
else
|
||||
line_out << ch;
|
||||
}
|
||||
|
||||
transformed_pos = pos;
|
||||
transformed_pos.rotate(angle);
|
||||
transformed_pos.translate(translation);
|
||||
|
||||
if (transformed_pos != old_pos) {
|
||||
line = line_out.str();
|
||||
char buf[2048] = "G1";
|
||||
if (transformed_pos.x != old_pos.x)
|
||||
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
|
||||
if (transformed_pos.y != old_pos.y)
|
||||
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
|
||||
|
||||
line.replace(line.find("G1 "), 3, buf);
|
||||
old_pos = transformed_pos;
|
||||
}
|
||||
}
|
||||
gcode_out += line + "\n";
|
||||
}
|
||||
return gcode_out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
||||
{
|
||||
assert(m_layer_idx == 0);
|
||||
@@ -309,10 +372,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||
size_t object_idx;
|
||||
size_t layer_idx;
|
||||
};
|
||||
std::vector<std::vector<LayerToPrint>> per_object(print.objects.size(), std::vector<LayerToPrint>());
|
||||
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
std::vector<std::vector<LayerToPrint>> per_object(printable_objects.size(), std::vector<LayerToPrint>());
|
||||
std::vector<OrderingItem> ordering;
|
||||
for (size_t i = 0; i < print.objects.size(); ++ i) {
|
||||
per_object[i] = collect_layers_to_print(*print.objects[i]);
|
||||
for (size_t i = 0; i < printable_objects.size(); ++i) {
|
||||
per_object[i] = collect_layers_to_print(*printable_objects[i]);
|
||||
OrderingItem ordering_item;
|
||||
ordering_item.object_idx = i;
|
||||
ordering.reserve(ordering.size() + per_object[i].size());
|
||||
@@ -337,8 +402,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
|
||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||
merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
|
||||
merged.second.assign(print.objects.size(), LayerToPrint());
|
||||
for (; i < j; ++ i) {
|
||||
merged.second.assign(printable_objects.size(), LayerToPrint());
|
||||
for (; i < j; ++i) {
|
||||
const OrderingItem &oi = ordering[i];
|
||||
assert(merged.second[oi.object_idx].layer() == nullptr);
|
||||
merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
|
||||
@@ -375,10 +440,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
if (print->config.remaining_times.value)
|
||||
{
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
}
|
||||
|
||||
if (! this->m_placeholder_parser_failed_templates.empty()) {
|
||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||
@@ -457,8 +524,21 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
// are considered to be active for the single extruder multi-material printers only.
|
||||
m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
|
||||
m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
// are considered to be active for the single extruder multi-material printers only.
|
||||
m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
|
||||
m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
|
||||
}
|
||||
|
||||
// resets analyzer
|
||||
m_analyzer.reset();
|
||||
@@ -472,9 +552,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
// How many times will be change_layer() called?
|
||||
// change_layer() in turn increments the progress bar status.
|
||||
m_layer_count = 0;
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
if (print.config.complete_objects.value) {
|
||||
// Add each of the object's layers separately.
|
||||
for (auto object : print.objects) {
|
||||
for (auto object : printable_objects) {
|
||||
std::vector<coordf_t> zs;
|
||||
zs.reserve(object->layers.size() + object->support_layers.size());
|
||||
for (auto layer : object->layers)
|
||||
@@ -487,7 +568,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
} else {
|
||||
// Print all objects with the same print_z together.
|
||||
std::vector<coordf_t> zs;
|
||||
for (auto object : print.objects) {
|
||||
for (auto object : printable_objects) {
|
||||
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
|
||||
for (auto layer : object->layers)
|
||||
zs.push_back(layer->print_z);
|
||||
@@ -506,8 +587,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
{
|
||||
// get the minimum cross-section used in the print
|
||||
std::vector<double> mm3_per_mm;
|
||||
for (auto object : print.objects) {
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
for (auto object : printable_objects) {
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
for (auto layer : object->layers) {
|
||||
auto layerm = layer->regions[region_id];
|
||||
@@ -567,7 +648,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
_write(file, "\n");
|
||||
}
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
const PrintObject *first_object = printable_objects.front();
|
||||
const double layer_height = first_object->config.layer_height.value;
|
||||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
@@ -596,20 +677,24 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
size_t initial_print_object_id = 0;
|
||||
bool has_wipe_tower = false;
|
||||
if (print.config.complete_objects.value) {
|
||||
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
|
||||
for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
|
||||
tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
|
||||
for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
|
||||
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Find tool ordering for all the objects at once, and the initial extruder ID.
|
||||
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
|
||||
tool_ordering = print.m_tool_ordering.empty() ?
|
||||
ToolOrdering(print, initial_extruder_id) :
|
||||
print.m_tool_ordering;
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||||
initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
|
||||
// The priming towers will be skipped.
|
||||
tool_ordering.all_extruders().back() :
|
||||
// Don't skip the priming towers.
|
||||
tool_ordering.first_extruder();
|
||||
}
|
||||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
// Nothing to print!
|
||||
@@ -637,6 +722,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
m_placeholder_parser.set("current_object_idx", 0);
|
||||
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
|
||||
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
||||
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
|
||||
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
|
||||
|
||||
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
||||
@@ -676,7 +762,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
// Collect outer contours of all objects over all layers.
|
||||
// Discard objects only containing thin walls (offset would fail on an empty polygon).
|
||||
Polygons islands;
|
||||
for (const PrintObject *object : print.objects)
|
||||
for (const PrintObject *object : printable_objects)
|
||||
for (const Layer *layer : object->layers)
|
||||
for (const ExPolygon &expoly : layer->slices.expolygons)
|
||||
for (const Point © : object->_shifted_copies) {
|
||||
@@ -717,14 +803,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial extruder only after custom start G-code.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
|
||||
// Set initial extruder only after custom start G-code.
|
||||
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
}
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config.complete_objects.value) {
|
||||
// Print objects from the smallest to the tallest to avoid collisions
|
||||
// when moving onto next object starting point.
|
||||
std::vector<PrintObject*> objects(print.objects);
|
||||
std::vector<PrintObject*> objects(printable_objects);
|
||||
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });
|
||||
size_t finished_objects = 0;
|
||||
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
|
||||
@@ -788,7 +877,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
for (PrintObject *object : printable_objects)
|
||||
object_reference_points.push_back(object->_shifted_copies.front());
|
||||
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
|
||||
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
|
||||
// Sort layers by Z.
|
||||
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||
@@ -796,27 +885,29 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
if (print.config.single_extruder_multi_material_priming) {
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
@@ -996,9 +1087,10 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
||||
int(print.config.machine_max_feedrate_y.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_z.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_e.values.front() + 0.5));
|
||||
fprintf(file, "M204 S%d T%d ; sets acceleration (S) and retract acceleration (T), mm/sec^2\n",
|
||||
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
|
||||
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5));
|
||||
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config.machine_max_jerk_x.values.front(),
|
||||
print.config.machine_max_jerk_y.values.front(),
|
||||
|
||||
@@ -83,8 +83,10 @@ public:
|
||||
const WipeTower::ToolChangeResult &priming,
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
|
||||
const WipeTower::ToolChangeResult &final_purge) :
|
||||
m_left(float(print_config.wipe_tower_x.value)),
|
||||
m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
|
||||
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
|
||||
m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
|
||||
m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
|
||||
m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
|
||||
m_priming(priming),
|
||||
m_tool_changes(tool_changes),
|
||||
m_final_purge(final_purge),
|
||||
@@ -101,9 +103,14 @@ private:
|
||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
|
||||
|
||||
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
|
||||
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
|
||||
|
||||
// Left / right edges of the wipe tower, for the planning of wipe moves.
|
||||
const float m_left;
|
||||
const float m_right;
|
||||
const WipeTower::xy m_wipe_tower_pos;
|
||||
const float m_wipe_tower_rotation;
|
||||
// Reference to cached values at the Printer class.
|
||||
const WipeTower::ToolChangeResult &m_priming;
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
|
||||
@@ -112,6 +119,7 @@ private:
|
||||
int m_layer_idx;
|
||||
int m_tool_change_idx;
|
||||
bool m_brim_done;
|
||||
bool i_have_brim = false;
|
||||
};
|
||||
|
||||
class GCode {
|
||||
|
||||
@@ -134,6 +134,11 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
|
||||
// The projection does not contain the priming regions.
|
||||
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
|
||||
{
|
||||
// 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.
|
||||
Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value);
|
||||
float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value;
|
||||
|
||||
BoundingBoxf bbox;
|
||||
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
|
||||
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
|
||||
@@ -144,6 +149,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
|
||||
if (e.width > 0) {
|
||||
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
|
||||
Pointf p2(e.pos.x, e.pos.y);
|
||||
p1.rotate(wipe_tower_angle);
|
||||
p1.translate(wipe_tower_pos);
|
||||
p2.rotate(wipe_tower_angle);
|
||||
p2.translate(wipe_tower_pos);
|
||||
|
||||
bbox.merge(p1);
|
||||
coordf_t radius = 0.5 * e.width;
|
||||
bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);
|
||||
|
||||
@@ -451,10 +451,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
||||
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
|
||||
|
||||
// we will sort objects so that dedicated for wiping are at the beginning:
|
||||
PrintObjectPtrs object_list = print.objects;
|
||||
PrintObjectPtrs object_list = print.get_printable_objects();
|
||||
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
|
||||
|
||||
|
||||
// We will now iterate through
|
||||
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
|
||||
// - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
|
||||
@@ -548,7 +547,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
||||
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
|
||||
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
|
||||
|
||||
for (const PrintObject* object : print.objects) {
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
for (const PrintObject* object : printable_objects) {
|
||||
// Finds this layer:
|
||||
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
|
||||
if (this_layer_it == object->layers.end())
|
||||
|
||||
@@ -66,8 +66,10 @@ public:
|
||||
wipe_tower_partitions(0),
|
||||
wipe_tower_layer_height(0.) {}
|
||||
|
||||
bool operator< (const LayerTools &rhs) const { return print_z - EPSILON < rhs.print_z; }
|
||||
bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
|
||||
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
|
||||
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
|
||||
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
|
||||
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
|
||||
|
||||
bool is_extruder_order(unsigned int a, unsigned int b) const;
|
||||
|
||||
|
||||
@@ -25,18 +25,30 @@ public:
|
||||
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
|
||||
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
|
||||
|
||||
// Rotate the point around given point about given angle (in degrees)
|
||||
// shifts the result so that point of rotation is in the middle of the tower
|
||||
xy rotate(const xy& origin, float width, float depth, float angle) const {
|
||||
// Rotate the point around center of the wipe tower about given angle (in degrees)
|
||||
xy rotate(float width, float depth, float angle) const {
|
||||
xy out(0,0);
|
||||
float temp_x = x - width / 2.f;
|
||||
float temp_y = y - depth / 2.f;
|
||||
angle *= M_PI/180.;
|
||||
out.x += (temp_x - origin.x) * cos(angle) - (temp_y - origin.y) * sin(angle);
|
||||
out.y += (temp_x - origin.x) * sin(angle) + (temp_y - origin.y) * cos(angle);
|
||||
return out + origin;
|
||||
out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
|
||||
out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Rotate the point around origin about given angle in degrees
|
||||
void rotate(float angle) {
|
||||
float temp_x = x * cos(angle) - y * sin(angle);
|
||||
y = x * sin(angle) + y * cos(angle);
|
||||
x = temp_x;
|
||||
}
|
||||
|
||||
void translate(const xy& vect) {
|
||||
x += vect.x;
|
||||
y += vect.y;
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
@@ -104,6 +116,9 @@ public:
|
||||
// This is useful not only for the print time estimation, but also for the control of layer cooling.
|
||||
float elapsed_time;
|
||||
|
||||
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
|
||||
bool priming;
|
||||
|
||||
// Sum the total length of the extrusion.
|
||||
float total_extrusion_length_in_plane() {
|
||||
float e_length = 0.f;
|
||||
|
||||
@@ -5,7 +5,7 @@ TODO LIST
|
||||
|
||||
1. cooling moves - DONE
|
||||
2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
|
||||
3. priming extrusions (last wipe must clear the color)
|
||||
3. priming extrusions (last wipe must clear the color) - DONE
|
||||
4. Peter's wipe tower - layer's are not exactly square
|
||||
5. Peter's wipe tower - variable width for higher levels
|
||||
6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
|
||||
@@ -17,7 +17,6 @@ TODO LIST
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
@@ -68,8 +67,11 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& set_initial_position(const WipeTower::xy &pos) {
|
||||
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg);
|
||||
Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
|
||||
m_wipe_tower_width = width;
|
||||
m_wipe_tower_depth = depth;
|
||||
m_internal_angle = internal_angle;
|
||||
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
|
||||
m_current_pos = pos;
|
||||
return *this;
|
||||
}
|
||||
@@ -81,9 +83,6 @@ public:
|
||||
|
||||
Writer& set_extrusion_flow(float flow)
|
||||
{ m_extrusion_flow = flow; return *this; }
|
||||
|
||||
Writer& set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
|
||||
{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
|
||||
|
||||
Writer& set_y_shift(float shift) {
|
||||
m_current_pos.y -= shift-m_y_shift;
|
||||
@@ -110,7 +109,7 @@ public:
|
||||
float y() const { return m_current_pos.y; }
|
||||
const WipeTower::xy& pos() const { return m_current_pos; }
|
||||
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
|
||||
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
|
||||
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
|
||||
float elapsed_time() const { return m_elapsed_time; }
|
||||
|
||||
// Extrude with an explicitely provided amount of extrusion.
|
||||
@@ -125,9 +124,9 @@ public:
|
||||
double len = sqrt(dx*dx+dy*dy);
|
||||
|
||||
|
||||
// For rotated wipe tower, transform position to printer coordinates
|
||||
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we are
|
||||
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we want to go
|
||||
// Now do the "internal rotation" with respect to the wipe tower center
|
||||
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
|
||||
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
|
||||
|
||||
if (! m_preview_suppressed && e > 0.f && len > 0.) {
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
@@ -147,6 +146,7 @@ public:
|
||||
if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
|
||||
m_gcode += set_format_Y(rot.y);
|
||||
|
||||
|
||||
if (e != 0.f)
|
||||
m_gcode += set_format_E(e);
|
||||
|
||||
@@ -397,9 +397,8 @@ private:
|
||||
std::string m_gcode;
|
||||
std::vector<WipeTower::Extrusion> m_extrusions;
|
||||
float m_elapsed_time;
|
||||
float m_angle_deg = 0.f;
|
||||
float m_internal_angle = 0.f;
|
||||
float m_y_shift = 0.f;
|
||||
WipeTower::xy m_wipe_tower_pos;
|
||||
float m_wipe_tower_width = 0.f;
|
||||
float m_wipe_tower_depth = 0.f;
|
||||
float m_last_fan_speed = 0.f;
|
||||
@@ -490,7 +489,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
|
||||
|
||||
const float prime_section_width = std::min(240.f / tools.size(), 60.f);
|
||||
box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
|
||||
box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
|
||||
|
||||
PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
@@ -539,6 +538,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||
m_print_brim = true;
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = true;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
@@ -575,7 +575,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
}
|
||||
|
||||
box_coordinates cleaning_box(
|
||||
m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
m_wipe_tower_width - m_perimeter_width,
|
||||
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
|
||||
: m_wipe_tower_depth-m_perimeter_width));
|
||||
@@ -584,7 +584,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP TOOLCHANGE START\n")
|
||||
@@ -594,7 +593,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
.speed_override(100);
|
||||
|
||||
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
|
||||
writer.set_initial_position(initial_position);
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
// Increase the extruder driver current to allow fast ramming.
|
||||
writer.set_extruder_trimpot(750);
|
||||
@@ -616,11 +615,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
if (last_change_in_layer) {// draw perimeter line
|
||||
writer.set_y_shift(m_y_shift);
|
||||
if (m_peters_wipe_tower)
|
||||
writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
|
||||
writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
|
||||
else {
|
||||
writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
|
||||
writer.travel(m_wipe_tower_pos.x + (writer.x()> (m_wipe_tower_pos.x + m_wipe_tower_width) / 2.f ? 0.f : m_wipe_tower_width), writer.y());
|
||||
writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,6 +633,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
"\n\n");
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
@@ -647,7 +647,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
|
||||
{
|
||||
const box_coordinates wipeTower_box(
|
||||
m_wipe_tower_pos,
|
||||
WipeTower::xy(0.f, 0.f),
|
||||
m_wipe_tower_width,
|
||||
m_wipe_tower_depth);
|
||||
|
||||
@@ -655,12 +655,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
||||
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
|
||||
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.append(";-------------------------------------\n"
|
||||
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
|
||||
|
||||
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
|
||||
writer.set_initial_position(initial_position);
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
|
||||
1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
|
||||
@@ -685,6 +684,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
||||
m_print_brim = false; // Mark the brim as extruded
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
@@ -724,7 +724,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
||||
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
|
||||
|
||||
// this is y of the center of previous sparse infill border
|
||||
float sparse_beginning_y = m_wipe_tower_pos.y;
|
||||
float sparse_beginning_y = 0.f;
|
||||
if (m_current_shape == SHAPE_REVERSED)
|
||||
sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
|
||||
- ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
|
||||
@@ -742,7 +742,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
||||
for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
|
||||
if (tch.old_tool == m_current_tool) {
|
||||
sum_of_depths += tch.ramming_depth;
|
||||
float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
|
||||
float ramming_end_y = sum_of_depths;
|
||||
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
|
||||
|
||||
// debugging:
|
||||
@@ -793,11 +793,16 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
||||
float turning_point = (!m_left_to_right ? xl : xr );
|
||||
float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
|
||||
writer.suppress_preview()
|
||||
.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
|
||||
.retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
|
||||
.retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
.retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
.retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
|
||||
/*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
|
||||
.load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
|
||||
.load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
|
||||
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
|
||||
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
|
||||
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
|
||||
.resume_preview();
|
||||
|
||||
if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
|
||||
@@ -874,10 +879,15 @@ void WipeTowerPrusaMM::toolchange_Load(
|
||||
|
||||
writer.append("; CP TOOLCHANGE LOAD\n")
|
||||
.suppress_preview()
|
||||
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
|
||||
/*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
|
||||
.load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
|
||||
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
|
||||
|
||||
.load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
|
||||
.load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
|
||||
|
||||
.travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
|
||||
.resume_preview();
|
||||
|
||||
@@ -950,7 +960,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
||||
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
|
||||
m_left_to_right = !m_left_to_right;
|
||||
writer.travel(writer.x(), writer.y() - dy)
|
||||
.travel(m_wipe_tower_pos.x + (m_left_to_right ? m_wipe_tower_width : 0.f), writer.y());
|
||||
.travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
|
||||
}
|
||||
|
||||
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
|
||||
@@ -969,7 +979,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP EMPTY GRID START\n")
|
||||
@@ -978,14 +987,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
||||
// Slow down on the 1st layer.
|
||||
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
|
||||
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
|
||||
box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
|
||||
box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
|
||||
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
|
||||
|
||||
if (m_left_to_right) // so there is never a diagonal travel
|
||||
writer.set_initial_position(fill_box.ru);
|
||||
else
|
||||
writer.set_initial_position(fill_box.lu);
|
||||
|
||||
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
|
||||
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
box_coordinates box = fill_box;
|
||||
for (int i=0;i<2;++i) {
|
||||
@@ -1044,6 +1051,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
||||
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
@@ -1165,9 +1173,9 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
||||
{
|
||||
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
|
||||
if (m_peters_wipe_tower)
|
||||
m_wipe_tower_rotation_angle += 90.f;
|
||||
m_internal_rotation += 90.f;
|
||||
else
|
||||
m_wipe_tower_rotation_angle += 180.f;
|
||||
m_internal_rotation += 180.f;
|
||||
|
||||
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
|
||||
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
|
||||
@@ -1188,7 +1196,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
||||
last_toolchange.gcode += buf;
|
||||
}
|
||||
last_toolchange.gcode += finish_layer_toolchange.gcode;
|
||||
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(),finish_layer_toolchange.extrusions.begin(),finish_layer_toolchange.extrusions.end());
|
||||
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
|
||||
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -65,9 +65,9 @@ public:
|
||||
|
||||
|
||||
// Set the extruder properties.
|
||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
|
||||
float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
|
||||
float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
|
||||
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
|
||||
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||
{
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
@@ -76,7 +76,9 @@ public:
|
||||
m_filpar[idx].temperature = temp;
|
||||
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
||||
m_filpar[idx].loading_speed = loading_speed;
|
||||
m_filpar[idx].loading_speed_start = loading_speed_start;
|
||||
m_filpar[idx].unloading_speed = unloading_speed;
|
||||
m_filpar[idx].unloading_speed_start = unloading_speed_start;
|
||||
m_filpar[idx].delay = delay;
|
||||
m_filpar[idx].cooling_moves = cooling_moves;
|
||||
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
|
||||
@@ -102,6 +104,8 @@ public:
|
||||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
|
||||
|
||||
|
||||
// Switch to a next layer.
|
||||
@@ -189,6 +193,7 @@ private:
|
||||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
||||
float m_internal_rotation = 0.f;
|
||||
float m_y_shift = 0.f; // y shift passed to writer
|
||||
float m_z_pos = 0.f; // Current Z position.
|
||||
float m_layer_height = 0.f; // Current layer height.
|
||||
@@ -213,7 +218,9 @@ private:
|
||||
int temperature = 0;
|
||||
int first_layer_temperature = 0;
|
||||
float loading_speed = 0.f;
|
||||
float loading_speed_start = 0.f;
|
||||
float unloading_speed = 0.f;
|
||||
float unloading_speed_start = 0.f;
|
||||
float delay = 0.f ;
|
||||
int cooling_moves = 0;
|
||||
float cooling_initial_speed = 0.f;
|
||||
|
||||
@@ -114,6 +114,28 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has(char axis) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = skip_word(c);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
if (*c == axis)
|
||||
return true;
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
|
||||
float value(Axis axis) const { return m_axis[axis]; }
|
||||
bool has(char axis) const;
|
||||
bool has_value(char axis, float &value) const;
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
@@ -568,16 +569,12 @@ GCodeSender::set_DTR(bool on)
|
||||
void
|
||||
GCodeSender::reset()
|
||||
{
|
||||
this->set_DTR(false);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
||||
this->set_DTR(true);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
||||
this->set_DTR(false);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -469,6 +469,40 @@ namespace Slic3r {
|
||||
return _state.minimum_travel_feedrate;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
|
||||
{
|
||||
_state.filament_load_times.clear();
|
||||
for (double t : filament_load_times)
|
||||
_state.filament_load_times.push_back(t);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
|
||||
{
|
||||
_state.filament_unload_times.clear();
|
||||
for (double t : filament_unload_times)
|
||||
_state.filament_unload_times.push_back(t);
|
||||
}
|
||||
|
||||
float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
|
||||
{
|
||||
return
|
||||
(_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
|
||||
0 :
|
||||
(_state.filament_load_times.size() <= id_extruder) ?
|
||||
_state.filament_load_times.front() :
|
||||
_state.filament_load_times[id_extruder];
|
||||
}
|
||||
|
||||
float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
|
||||
{
|
||||
return
|
||||
(_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
|
||||
0 :
|
||||
(_state.filament_unload_times.size() <= id_extruder) ?
|
||||
_state.filament_unload_times.front() :
|
||||
_state.filament_unload_times[id_extruder];
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
|
||||
{
|
||||
_state.extrude_factor_override_percentage = percentage;
|
||||
@@ -535,6 +569,23 @@ namespace Slic3r {
|
||||
_state.g1_line_id = 0;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_extruder_id(unsigned int id)
|
||||
{
|
||||
_state.extruder_id = id;
|
||||
}
|
||||
|
||||
unsigned int GCodeTimeEstimator::get_extruder_id() const
|
||||
{
|
||||
return _state.extruder_id;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::reset_extruder_id()
|
||||
{
|
||||
// Set the initial extruder ID to unknown. For the multi-material setup it means
|
||||
// that all the filaments are parked in the MMU and no filament is loaded yet.
|
||||
_state.extruder_id = _state.extruder_id_unloaded;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::add_additional_time(float timeSec)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
@@ -575,6 +626,9 @@ namespace Slic3r {
|
||||
set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]);
|
||||
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
|
||||
}
|
||||
|
||||
_state.filament_load_times.clear();
|
||||
_state.filament_unload_times.clear();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::reset()
|
||||
@@ -613,6 +667,7 @@ namespace Slic3r {
|
||||
|
||||
set_additional_time(0.0f);
|
||||
|
||||
reset_extruder_id();
|
||||
reset_g1_line_id();
|
||||
_g1_line_ids.clear();
|
||||
|
||||
@@ -666,6 +721,8 @@ namespace Slic3r {
|
||||
}
|
||||
|
||||
_last_st_synchronized_block_id = _blocks.size() - 1;
|
||||
// The additional time has been consumed (added to the total time), reset it to zero.
|
||||
set_additional_time(0.);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
|
||||
@@ -778,8 +835,18 @@ namespace Slic3r {
|
||||
_processM566(line);
|
||||
break;
|
||||
}
|
||||
case 702: // MK3 MMU2: Process the final filament unload.
|
||||
{
|
||||
_processM702(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'T': // Select Tools
|
||||
{
|
||||
_processT(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1164,11 +1231,25 @@ namespace Slic3r {
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
float value;
|
||||
if (line.has_value('S', value))
|
||||
if (line.has_value('S', value)) {
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
|
||||
// and it is also generated by Slic3r to control acceleration per extrusion type
|
||||
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
|
||||
set_acceleration(value);
|
||||
|
||||
if (line.has_value('T', value))
|
||||
set_retract_acceleration(value);
|
||||
if (line.has_value('T', value))
|
||||
set_retract_acceleration(value);
|
||||
} else {
|
||||
// New acceleration format, compatible with the upstream Marlin.
|
||||
if (line.has_value('P', value))
|
||||
set_acceleration(value);
|
||||
if (line.has_value('R', value))
|
||||
set_retract_acceleration(value);
|
||||
if (line.has_value('T', value)) {
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
|
||||
// set_travel_acceleration(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
|
||||
@@ -1223,6 +1304,37 @@ namespace Slic3r {
|
||||
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
if (line.has('C')) {
|
||||
// MK3 MMU2 specific M code:
|
||||
// M702 C is expected to be sent by the custom end G-code when finalizing a print.
|
||||
// The MK3 unit shall unload and park the active filament into the MMU2 unit.
|
||||
add_additional_time(get_filament_unload_time(get_extruder_id()));
|
||||
reset_extruder_id();
|
||||
_simulate_st_synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processT(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
std::string cmd = line.cmd();
|
||||
if (cmd.length() > 1)
|
||||
{
|
||||
unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
|
||||
if (get_extruder_id() != id)
|
||||
{
|
||||
// Specific to the MK3 MMU2: The initial extruder ID is set to -1 indicating
|
||||
// that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet.
|
||||
add_additional_time(get_filament_unload_time(get_extruder_id()));
|
||||
set_extruder_id(id);
|
||||
add_additional_time(get_filament_load_time(get_extruder_id()));
|
||||
_simulate_st_synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_simulate_st_synchronize()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
@@ -79,7 +79,15 @@ namespace Slic3r {
|
||||
float minimum_feedrate; // mm/s
|
||||
float minimum_travel_feedrate; // mm/s
|
||||
float extrude_factor_override_percentage;
|
||||
// Additional load / unload times for a filament exchange sequence.
|
||||
std::vector<float> filament_load_times;
|
||||
std::vector<float> filament_unload_times;
|
||||
unsigned int g1_line_id;
|
||||
// extruder_id is currently used to correctly calculate filament load / unload times
|
||||
// into the total print time. This is currently only really used by the MK3 MMU2:
|
||||
// Extruder id (-1) means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
|
||||
static const unsigned int extruder_id_unloaded = (unsigned int)-1;
|
||||
unsigned int extruder_id;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -281,6 +289,11 @@ namespace Slic3r {
|
||||
void set_minimum_travel_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_travel_feedrate() const;
|
||||
|
||||
void set_filament_load_times(const std::vector<double> &filament_load_times);
|
||||
void set_filament_unload_times(const std::vector<double> &filament_unload_times);
|
||||
float get_filament_load_time(unsigned int id_extruder);
|
||||
float get_filament_unload_time(unsigned int id_extruder);
|
||||
|
||||
void set_extrude_factor_override_percentage(float percentage);
|
||||
float get_extrude_factor_override_percentage() const;
|
||||
|
||||
@@ -300,6 +313,10 @@ namespace Slic3r {
|
||||
void increment_g1_line_id();
|
||||
void reset_g1_line_id();
|
||||
|
||||
void set_extruder_id(unsigned int id);
|
||||
unsigned int get_extruder_id() const;
|
||||
void reset_extruder_id();
|
||||
|
||||
void add_additional_time(float timeSec);
|
||||
void set_additional_time(float timeSec);
|
||||
float get_additional_time() const;
|
||||
@@ -383,6 +400,12 @@ namespace Slic3r {
|
||||
// Set allowable instantaneous speed change
|
||||
void _processM566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
void _processM702(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
void _processT(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void _simulate_st_synchronize();
|
||||
|
||||
|
||||
@@ -195,47 +195,62 @@ using namespace boost::polygon; // provides also high() and low()
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
static bool
|
||||
sort_points (Point a, Point b)
|
||||
{
|
||||
return (a.x < b.x) || (a.x == b.x && a.y < b.y);
|
||||
}
|
||||
struct SortPoints {
|
||||
template <class T>
|
||||
bool operator()(const T& a, const T& b) const {
|
||||
return (b.x > a.x) || (a.x == b.x && b.y > a.y);
|
||||
}
|
||||
};
|
||||
|
||||
/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
template<class T>
|
||||
static T raw_convex_hull(T& points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), sort_points);
|
||||
std::sort(points.begin(), points.end(), SortPoints());
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Polygon hull;
|
||||
T hull;
|
||||
|
||||
if (n >= 3) {
|
||||
hull.points.resize(2*n);
|
||||
hull.resize(2*n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; i++) {
|
||||
while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||
hull.points[k++] = points[i];
|
||||
while (k >= 2 && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||
hull.points[k++] = points[i];
|
||||
while (k >= t && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.points.resize(k);
|
||||
hull.resize(k);
|
||||
|
||||
assert( hull.points.front().coincides_with(hull.points.back()) );
|
||||
hull.points.pop_back();
|
||||
assert( hull.front().coincides_with(hull.back()) );
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s
|
||||
convex_hull(Pointf3s points)
|
||||
{
|
||||
return raw_convex_hull(points);
|
||||
}
|
||||
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
{
|
||||
Polygon hull;
|
||||
hull.points = raw_convex_hull(points);
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon
|
||||
convex_hull(const Polygons &polygons)
|
||||
{
|
||||
@@ -243,7 +258,7 @@ convex_hull(const Polygons &polygons)
|
||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(pp);
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
/* accepts an arrayref of points and returns a list of indices
|
||||
|
||||
@@ -108,8 +108,10 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
|
||||
return true;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
|
||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
||||
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <libnest2d.h>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -22,6 +17,11 @@
|
||||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
unsigned int Model::s_auto_extruder_id = 1;
|
||||
@@ -240,14 +240,6 @@ BoundingBoxf3 Model::bounding_box() const
|
||||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 Model::transformed_bounding_box() const
|
||||
{
|
||||
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();
|
||||
@@ -304,369 +296,36 @@ static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace arr {
|
||||
|
||||
using namespace libnest2d;
|
||||
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\n";
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
ss << "\t{\n";
|
||||
ss << "\t\t{\n";
|
||||
|
||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = expoly.contour.points.front();
|
||||
ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
// Holes:
|
||||
ss << "\t\t{\n";
|
||||
if(holes) for(auto h : expoly.holes) {
|
||||
ss << "\t\t\t{\n";
|
||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = h.points.front();
|
||||
ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t\t},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
ss << "\t},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
svg.draw(expolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
|
||||
[](size_t s, ModelObject* o){
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
ret.reserve(s);
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
ClipperLib::PolygonImpl pn;
|
||||
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = tmpmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
|
||||
|
||||
// Efficient conversion to item.
|
||||
Item item(std::move(pn));
|
||||
|
||||
// Invalid geometries would throw exceptions when arranging
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(objinst->rotation);
|
||||
item.translation( {
|
||||
ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Arranges the model objects on the screen.
|
||||
*
|
||||
* The arrangement considers multiple bins (aka. print beds) for placing all
|
||||
* the items provided in the model argument. If the items don't fit on one
|
||||
* print bed, the remaining will be placed onto newly created print beds.
|
||||
* The first_bin_only parameter, if set to true, disables this behaviour and
|
||||
* makes sure that only one print bed is filled and the remaining items will be
|
||||
* untouched. When set to false, the items which could not fit onto the
|
||||
* print bed will be placed next to the print bed so the user should see a
|
||||
* pile of items on the print bed and some other piles outside the print
|
||||
* area that can be dragged later onto the print bed as a group.
|
||||
*
|
||||
* \param model The model object with the 3D content.
|
||||
* \param dist The minimum distance which is allowed for any pair of items
|
||||
* on the print bed in any direction.
|
||||
* \param bb The bounding box of the print bed. It corresponds to the 'bin'
|
||||
* for bin packing.
|
||||
* \param first_bin_only This parameter controls whether to place the
|
||||
* remaining items which do not fit onto the print area next to the print
|
||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||
* them).
|
||||
*/
|
||||
bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
|
||||
bool first_bin_only,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Create the arranger config
|
||||
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
bool hasbin = bb != nullptr && bb->defined;
|
||||
double area_max = 0;
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
std::vector<std::reference_wrapper<Item>> shapes;
|
||||
shapes.reserve(shapemap.size());
|
||||
std::for_each(shapemap.begin(), shapemap.end(),
|
||||
[&shapes, min_obj_distance, &area_max, hasbin]
|
||||
(ShapeData2D::value_type& it)
|
||||
{
|
||||
shapes.push_back(std::ref(it.second));
|
||||
});
|
||||
|
||||
Box bin;
|
||||
|
||||
if(hasbin) {
|
||||
// Scale up the bounding box to clipper scale.
|
||||
BoundingBoxf bbb = *bb;
|
||||
bbb.scale(1.0/SCALING_FACTOR);
|
||||
|
||||
bin = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min.x),
|
||||
static_cast<libnest2d::Coord>(bbb.min.y)
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max.x),
|
||||
static_cast<libnest2d::Coord>(bbb.max.y)
|
||||
});
|
||||
}
|
||||
|
||||
// Will use the DJD selection heuristic with the BottomLeft placement
|
||||
// strategy
|
||||
using Arranger = Arranger<NfpPlacer, FirstFitSelection>;
|
||||
using PConf = Arranger::PlacementConfig;
|
||||
using SConf = Arranger::SelectionConfig;
|
||||
|
||||
PConf pcfg; // Placement configuration
|
||||
SConf scfg; // Selection configuration
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
||||
// Start placing the items from the center of the print bed
|
||||
pcfg.starting_point = PConf::Alignment::CENTER;
|
||||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations
|
||||
// arranger.useMinimumBoundigBoxRotation();
|
||||
pcfg.rotations = { 0.0 };
|
||||
|
||||
// Magic: we will specify what is the goal of arrangement... In this case
|
||||
// we override the default object function to make the larger items go into
|
||||
// the center of the pile and smaller items orbit it so the resulting pile
|
||||
// has a circle-like shape. This is good for the print bed's heat profile.
|
||||
// We alse sacrafice a bit of pack efficiency for this to work. As a side
|
||||
// effect, the arrange procedure is a lot faster (we do not need to
|
||||
// calculate the convex hulls)
|
||||
pcfg.object_function = [bin, hasbin](
|
||||
NfpPlacer::Pile pile, // The currently arranged pile
|
||||
double /*area*/, // Sum area of items (not needed)
|
||||
double norm, // A norming factor for physical dimensions
|
||||
double penality) // Min penality in case of bad arrangement
|
||||
{
|
||||
auto bb = ShapeLike::boundingBox(pile);
|
||||
|
||||
// We get the current item that's being evaluated.
|
||||
auto& sh = pile.back();
|
||||
|
||||
// We retrieve the reference point of this item
|
||||
auto rv = Nfp::referenceVertex(sh);
|
||||
|
||||
// We get the distance of the reference point from the center of the
|
||||
// heat bed
|
||||
auto c = bin.center();
|
||||
auto d = PointLike::distance(rv, c);
|
||||
|
||||
// The score will be the normalized distance which will be minimized,
|
||||
// effectively creating a circle shaped pile of items
|
||||
double score = double(d)/norm;
|
||||
|
||||
// If it does not fit into the print bed we will beat it
|
||||
// with a large penality. If we would not do this, there would be only
|
||||
// one big pile that doesn't care whether it fits onto the print bed.
|
||||
if(hasbin && !NfpPlacer::wouldFit(bb, bin)) score = 2*penality - score;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
// Create the arranger object
|
||||
Arranger arranger(bin, min_obj_distance, pcfg, scfg);
|
||||
|
||||
// Set the progress indicator for the arranger.
|
||||
arranger.progressIndicator(progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
auto result = arranger.arrangeIndexed(shapes.begin(), shapes.end());
|
||||
|
||||
auto applyResult = [&shapemap](ArrangeResult::value_type& group,
|
||||
Coord batch_offset)
|
||||
{
|
||||
for(auto& r : group) {
|
||||
auto idx = r.first; // get the original item index
|
||||
Item& item = r.second; // get the item itself
|
||||
|
||||
// Get the model instance from the shapemap using the index
|
||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
||||
|
||||
// Get the tranformation data from the item object and scale it
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Pointf foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
inst_ptr->offset = foff;
|
||||
}
|
||||
};
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0);
|
||||
} else {
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
|
||||
Coord stride = static_cast<Coord>(STRIDE_PADDING*
|
||||
bin.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
applyResult(group, batch_offset);
|
||||
|
||||
// Only the first pack group can be placed onto the print bed. The
|
||||
// other objects which could not fit will be placed next to the
|
||||
// print bed
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||
|
||||
return ret && result.size() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* arrange objects preserving their instance count
|
||||
but altering their instance positions */
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb,
|
||||
std::function<void(unsigned)> progressind)
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||
{
|
||||
bool ret = false;
|
||||
if(bb != nullptr && bb->defined) {
|
||||
// Despite the new arrange is able to run without a specified bin,
|
||||
// the perl testsuit still fails for this case. For now the safest
|
||||
// thing to do is to use the new arrange only when a proper bin is
|
||||
// specified.
|
||||
ret = arr::arrange(*this, dist, bb, false, progressind);
|
||||
} else {
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Duplicate the entire model preserving instance relative positions.
|
||||
@@ -961,54 +620,6 @@ const BoundingBoxf3& ModelObject::bounding_box() const
|
||||
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.x *= inst->scaling_factor;
|
||||
p.y *= inst->scaling_factor;
|
||||
p.z *= inst->scaling_factor;
|
||||
|
||||
// rotate Z
|
||||
double x = p.x;
|
||||
double y = p.y;
|
||||
p.x = c * x - s * y;
|
||||
p.y = s * x + c * y;
|
||||
|
||||
// translate
|
||||
p.x += inst->offset.x;
|
||||
p.y += inst->offset.y;
|
||||
|
||||
bb.merge(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh ModelObject::mesh() const
|
||||
{
|
||||
@@ -1093,25 +704,38 @@ void ModelObject::center_around_origin()
|
||||
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.translate(float(x), float(y), float(z));
|
||||
if (m_bounding_box_valid)
|
||||
v->m_convex_hull.translate(float(x), float(y), float(z));
|
||||
}
|
||||
|
||||
if (m_bounding_box_valid)
|
||||
m_bounding_box.translate(x, y, z);
|
||||
}
|
||||
|
||||
void ModelObject::scale(const Pointf3 &versor)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.scale(versor);
|
||||
v->m_convex_hull.scale(versor);
|
||||
}
|
||||
// reset origin translation since it doesn't make sense anymore
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::rotate(float angle, const Axis &axis)
|
||||
void ModelObject::rotate(float angle, const Pointf3& axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.rotate(angle, axis);
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
v->m_convex_hull.rotate(angle, axis);
|
||||
}
|
||||
|
||||
center_around_origin();
|
||||
|
||||
this->origin_translation = Pointf3(0, 0, 0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
@@ -1123,6 +747,7 @@ void ModelObject::transform(const float* matrix3x4)
|
||||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->mesh.transform(matrix3x4);
|
||||
v->m_convex_hull.transform(matrix3x4);
|
||||
}
|
||||
|
||||
origin_translation = Pointf3(0.0, 0.0, 0.0);
|
||||
@@ -1132,8 +757,12 @@ void ModelObject::transform(const float* matrix3x4)
|
||||
void ModelObject::mirror(const Axis &axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.mirror(axis);
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
v->m_convex_hull.mirror(axis);
|
||||
}
|
||||
|
||||
this->origin_translation = Pointf3(0, 0, 0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
@@ -1237,45 +866,20 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||
|
||||
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)
|
||||
{
|
||||
for (ModelInstance* inst : this->instances)
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
std::vector<float> world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX));
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
|
||||
m.translate(Eigen::Vector3f((float)inst->offset.x, (float)inst->offset.y, 0.0f));
|
||||
m.rotate(Eigen::AngleAxisf(inst->rotation, Eigen::Vector3f::UnitZ()));
|
||||
m.scale(inst->scaling_factor);
|
||||
::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
|
||||
|
||||
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.x *= inst->scaling_factor;
|
||||
p.y *= inst->scaling_factor;
|
||||
p.z *= inst->scaling_factor;
|
||||
|
||||
// rotate Z
|
||||
double x = p.x;
|
||||
double y = p.y;
|
||||
p.x = c * x - s * y;
|
||||
p.y = s * x + c * y;
|
||||
|
||||
// translate
|
||||
p.x += inst->offset.x;
|
||||
p.y += inst->offset.y;
|
||||
|
||||
bb.merge(p);
|
||||
}
|
||||
}
|
||||
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(world_mat);
|
||||
|
||||
if (print_volume.contains(bb))
|
||||
inst->print_volume_state = ModelInstance::PVS_Inside;
|
||||
@@ -1358,6 +962,16 @@ ModelMaterial* ModelVolume::assign_unique_material()
|
||||
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.
|
||||
// Return the number of volumes created from this one.
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
|
||||
@@ -105,9 +105,6 @@ public:
|
||||
// This bounding box is being cached.
|
||||
const BoundingBoxf3& bounding_box() const;
|
||||
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.
|
||||
TriangleMesh mesh() const;
|
||||
@@ -123,7 +120,7 @@ public:
|
||||
void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||
void scale(const Pointf3 &versor);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void rotate(float angle, const Pointf3& axis);
|
||||
void transform(const float* matrix3x4);
|
||||
void mirror(const Axis &axis);
|
||||
size_t materials_count() const;
|
||||
@@ -157,6 +154,10 @@ private:
|
||||
class ModelVolume
|
||||
{
|
||||
friend class ModelObject;
|
||||
|
||||
// The convex hull of this model's mesh.
|
||||
TriangleMesh m_convex_hull;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
// The triangular model.
|
||||
@@ -180,19 +181,32 @@ public:
|
||||
|
||||
ModelMaterial* assign_unique_material();
|
||||
|
||||
void calculate_convex_hull();
|
||||
const TriangleMesh& get_convex_hull() const;
|
||||
|
||||
private:
|
||||
// Parent object owning this ModelVolume.
|
||||
ModelObject* object;
|
||||
t_model_material_id _material_id;
|
||||
|
||||
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) {}
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||
name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object)
|
||||
{ this->material_id(other.material_id()); }
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(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) :
|
||||
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());
|
||||
}
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||
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.
|
||||
@@ -285,13 +299,10 @@ public:
|
||||
bool add_default_instances();
|
||||
// Returns approximate axis aligned bounding box of this model
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns tight axis aligned bounding box of this model
|
||||
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); }
|
||||
TriangleMesh mesh() const;
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL,
|
||||
std::function<void(unsigned)> progressind = [](unsigned){});
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
// Croaks if the duplicated objects do not fit the print bed.
|
||||
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
|
||||
597
xs/src/libslic3r/ModelArrange.hpp
Normal file
@@ -0,0 +1,597 @@
|
||||
#ifndef MODELARRANGE_HPP
|
||||
#define MODELARRANGE_HPP
|
||||
|
||||
#include "Model.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include <libnest2d.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <ClipperUtils.hpp>
|
||||
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace arr {
|
||||
|
||||
using namespace libnest2d;
|
||||
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\n";
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
ss << "\t{\n";
|
||||
ss << "\t\t{\n";
|
||||
|
||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = expoly.contour.points.front();
|
||||
ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
// Holes:
|
||||
ss << "\t\t{\n";
|
||||
if(holes) for(auto h : expoly.holes) {
|
||||
ss << "\t\t\t{\n";
|
||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = h.points.front();
|
||||
ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t\t},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
ss << "\t},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
svg.draw(expolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
using SpatElement = std::pair<Box, unsigned>;
|
||||
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
|
||||
|
||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||
objfunc(const PointImpl& bincenter,
|
||||
double /*bin_area*/,
|
||||
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
||||
double /*pile_area*/,
|
||||
const Item &item,
|
||||
double norm, // A norming factor for physical dimensions
|
||||
std::vector<double>& areacache, // pile item areas will be cached
|
||||
// a spatial index to quickly get neighbors of the candidate item
|
||||
SpatIndex& spatindex
|
||||
)
|
||||
{
|
||||
using pl = PointLike;
|
||||
using sl = ShapeLike;
|
||||
|
||||
static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
static const double ROUNDNESS_RATIO = 0.5;
|
||||
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
|
||||
|
||||
// We will treat big items (compared to the print bed) differently
|
||||
auto normarea = [norm](double area) { return std::sqrt(area)/norm; };
|
||||
|
||||
// If a new bin has been created:
|
||||
if(pile.size() < areacache.size()) {
|
||||
areacache.clear();
|
||||
spatindex.clear();
|
||||
}
|
||||
|
||||
// We must fill the caches:
|
||||
int idx = 0;
|
||||
for(auto& p : pile) {
|
||||
if(idx == areacache.size()) {
|
||||
areacache.emplace_back(sl::area(p));
|
||||
if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD)
|
||||
spatindex.insert({sl::boundingBox(p), idx});
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Candidate item bounding box
|
||||
auto ibb = item.boundingBox();
|
||||
|
||||
// Calculate the full bounding box of the pile with the candidate item
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto fullbb = ShapeLike::boundingBox(pile);
|
||||
pile.pop_back();
|
||||
|
||||
// The bounding box of the big items (they will accumulate in the center
|
||||
// of the pile
|
||||
Box bigbb;
|
||||
if(spatindex.empty()) bigbb = fullbb;
|
||||
else {
|
||||
auto boostbb = spatindex.bounds();
|
||||
boost::geometry::convert(boostbb, bigbb);
|
||||
}
|
||||
|
||||
// The size indicator of the candidate item. This is not the area,
|
||||
// but almost...
|
||||
double item_normarea = normarea(item.area());
|
||||
|
||||
// Will hold the resulting score
|
||||
double score = 0;
|
||||
|
||||
if(item_normarea > BIG_ITEM_TRESHOLD) {
|
||||
// This branch is for the bigger items..
|
||||
// Here we will use the closest point of the item bounding box to
|
||||
// the already arranged pile. So not the bb center nor the a choosen
|
||||
// corner but whichever is the closest to the center. This will
|
||||
// prevent some unwanted strange arrangements.
|
||||
|
||||
auto minc = ibb.minCorner(); // bottom left corner
|
||||
auto maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// top left and bottom right corners
|
||||
auto top_left = PointImpl{getX(minc), getY(maxc)};
|
||||
auto bottom_right = PointImpl{getX(maxc), getY(minc)};
|
||||
|
||||
// Now the distance of the gravity center will be calculated to the
|
||||
// five anchor points and the smallest will be chosen.
|
||||
std::array<double, 5> dists;
|
||||
auto cc = fullbb.center(); // The gravity center
|
||||
dists[0] = pl::distance(minc, cc);
|
||||
dists[1] = pl::distance(maxc, cc);
|
||||
dists[2] = pl::distance(ibb.center(), cc);
|
||||
dists[3] = pl::distance(top_left, cc);
|
||||
dists[4] = pl::distance(bottom_right, cc);
|
||||
|
||||
// The smalles distance from the arranged pile center:
|
||||
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// Density is the pack density: how big is the arranged pile
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
|
||||
// Prepare a variable for the alignment score.
|
||||
// This will indicate: how well is the candidate item aligned with
|
||||
// its neighbors. We will check the aligment with all neighbors and
|
||||
// return the score for the best alignment. So it is enough for the
|
||||
// candidate to be aligned with only one item.
|
||||
auto alignment_score = std::numeric_limits<double>::max();
|
||||
|
||||
auto& trsh = item.transformedShape();
|
||||
|
||||
auto querybb = item.boundingBox();
|
||||
|
||||
// Query the spatial index for the neigbours
|
||||
std::vector<SpatElement> result;
|
||||
spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
|
||||
|
||||
for(auto& e : result) { // now get the score for the best alignment
|
||||
auto idx = e.second;
|
||||
auto& p = pile[idx];
|
||||
auto parea = areacache[idx];
|
||||
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
|
||||
auto bbarea = bb.area();
|
||||
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
||||
|
||||
if(ascore < alignment_score) alignment_score = ascore;
|
||||
}
|
||||
|
||||
// The final mix of the score is the balance between the distance
|
||||
// from the full pile center, the pack density and the
|
||||
// alignment with the neigbours
|
||||
auto C = 0.33;
|
||||
score = C * dist + C * density + C * alignment_score;
|
||||
|
||||
} else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) {
|
||||
// If there are no big items, only small, we should consider the
|
||||
// density here as well to not get silly results
|
||||
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density;
|
||||
} else {
|
||||
// Here there are the small items that should be placed around the
|
||||
// already processed bigger items.
|
||||
// No need to play around with the anchor points, the center will be
|
||||
// just fine for small items
|
||||
score = pl::distance(ibb.center(), bigbb.center()) / norm;
|
||||
}
|
||||
|
||||
return std::make_tuple(score, fullbb);
|
||||
}
|
||||
|
||||
template<class PConf>
|
||||
void fillConfig(PConf& pcfg) {
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
||||
// Start placing the items from the center of the print bed
|
||||
pcfg.starting_point = PConf::Alignment::CENTER;
|
||||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations
|
||||
// arranger.useMinimumBoundigBoxRotation();
|
||||
pcfg.rotations = { 0.0 };
|
||||
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.6f;
|
||||
}
|
||||
|
||||
template<class TBin>
|
||||
class AutoArranger {};
|
||||
|
||||
template<class TBin>
|
||||
class _ArrBase {
|
||||
protected:
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
||||
using Selector = FirstFitSelection;
|
||||
using Packer = Arranger<Placer, Selector>;
|
||||
using PConfig = typename Packer::PlacementConfig;
|
||||
using Distance = TCoord<PointImpl>;
|
||||
using Pile = ShapeLike::Shapes<PolygonImpl>;
|
||||
|
||||
Packer pck_;
|
||||
PConfig pconf_; // Placement configuration
|
||||
double bin_area_;
|
||||
std::vector<double> areacache_;
|
||||
SpatIndex rtree_;
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
|
||||
{
|
||||
fillConfig(pconf_);
|
||||
pck_.progressIndicator(progressind);
|
||||
}
|
||||
|
||||
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
||||
areacache_.clear();
|
||||
return pck_.arrangeIndexed(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<Box>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(const Box& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
double score = std::get<0>(result);
|
||||
auto& fullbb = std::get<1>(result);
|
||||
|
||||
auto wdiff = fullbb.width() - bin.width();
|
||||
auto hdiff = fullbb.height() - bin.height();
|
||||
if(wdiff > 0) score += std::pow(wdiff, 2) / norm;
|
||||
if(hdiff > 0) score += std::pow(hdiff, 2) / norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||
public:
|
||||
AutoArranger(const PolygonImpl& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, &bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto binbb = ShapeLike::boundingBox(bin);
|
||||
auto result = objfunc(binbb.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
double score = std::get<0>(result);
|
||||
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto chull = ShapeLike::convexHull(pile);
|
||||
pile.pop_back();
|
||||
|
||||
// If it does not fit into the print bed we will beat it with a
|
||||
// large penality. If we would not do this, there would be only one
|
||||
// big pile that doesn't care whether it fits onto the print bed.
|
||||
if(!Placer::wouldFit(chull, bin)) score += norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
template<> // Specialization with no bin
|
||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
||||
{
|
||||
this->pconf_.object_function = [this] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto result = objfunc({0, 0}, 0, pile, pile_area,
|
||||
item, norm, areacache_, rtree_);
|
||||
return std::get<0>(result);
|
||||
};
|
||||
|
||||
this->pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
|
||||
[](size_t s, ModelObject* o){
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
ret.reserve(s);
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
ClipperLib::PolygonImpl pn;
|
||||
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = tmpmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
|
||||
|
||||
// Efficient conversion to item.
|
||||
Item item(std::move(pn));
|
||||
|
||||
// Invalid geometries would throw exceptions when arranging
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(objinst->rotation);
|
||||
item.translation( {
|
||||
ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum BedShapeHint {
|
||||
BOX,
|
||||
CIRCLE,
|
||||
IRREGULAR,
|
||||
WHO_KNOWS
|
||||
};
|
||||
|
||||
BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) {
|
||||
// Determine the bed shape by hand
|
||||
return BOX;
|
||||
}
|
||||
|
||||
void applyResult(
|
||||
IndexedPackGroup::value_type& group,
|
||||
Coord batch_offset,
|
||||
ShapeData2D& shapemap)
|
||||
{
|
||||
for(auto& r : group) {
|
||||
auto idx = r.first; // get the original item index
|
||||
Item& item = r.second; // get the item itself
|
||||
|
||||
// Get the model instance from the shapemap using the index
|
||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
||||
|
||||
// Get the tranformation data from the item object and scale it
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Pointf foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
inst_ptr->offset = foff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Arranges the model objects on the screen.
|
||||
*
|
||||
* The arrangement considers multiple bins (aka. print beds) for placing all
|
||||
* the items provided in the model argument. If the items don't fit on one
|
||||
* print bed, the remaining will be placed onto newly created print beds.
|
||||
* The first_bin_only parameter, if set to true, disables this behaviour and
|
||||
* makes sure that only one print bed is filled and the remaining items will be
|
||||
* untouched. When set to false, the items which could not fit onto the
|
||||
* print bed will be placed next to the print bed so the user should see a
|
||||
* pile of items on the print bed and some other piles outside the print
|
||||
* area that can be dragged later onto the print bed as a group.
|
||||
*
|
||||
* \param model The model object with the 3D content.
|
||||
* \param dist The minimum distance which is allowed for any pair of items
|
||||
* on the print bed in any direction.
|
||||
* \param bb The bounding box of the print bed. It corresponds to the 'bin'
|
||||
* for bin packing.
|
||||
* \param first_bin_only This parameter controls whether to place the
|
||||
* remaining items which do not fit onto the print area next to the print
|
||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||
* them).
|
||||
*/
|
||||
bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
const Slic3r::Polyline& bed,
|
||||
BedShapeHint bedhint,
|
||||
bool first_bin_only,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
std::vector<std::reference_wrapper<Item>> shapes;
|
||||
shapes.reserve(shapemap.size());
|
||||
std::for_each(shapemap.begin(), shapemap.end(),
|
||||
[&shapes] (ShapeData2D::value_type& it)
|
||||
{
|
||||
shapes.push_back(std::ref(it.second));
|
||||
});
|
||||
|
||||
IndexedPackGroup result;
|
||||
BoundingBox bbb(bed.points);
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min.x),
|
||||
static_cast<libnest2d::Coord>(bbb.min.y)
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max.x),
|
||||
static_cast<libnest2d::Coord>(bbb.max.y)
|
||||
});
|
||||
|
||||
switch(bedhint) {
|
||||
case BOX: {
|
||||
|
||||
// Create the arranger for the box shaped bed
|
||||
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
case CIRCLE:
|
||||
break;
|
||||
case IRREGULAR:
|
||||
case WHO_KNOWS: {
|
||||
using P = libnest2d::PolygonImpl;
|
||||
|
||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||
P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour));
|
||||
|
||||
// std::cout << ShapeLike::toString(irrbed) << std::endl;
|
||||
|
||||
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0, shapemap);
|
||||
} else {
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
|
||||
Coord stride = static_cast<Coord>(STRIDE_PADDING*
|
||||
binbb.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
applyResult(group, batch_offset, shapemap);
|
||||
|
||||
// Only the first pack group can be placed onto the print bed. The
|
||||
// other objects which could not fit will be placed next to the
|
||||
// print bed
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||
|
||||
return ret && result.size() == 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // MODELARRANGE_HPP
|
||||
@@ -263,6 +263,12 @@ operator<<(std::ostream &stm, const Pointf &pointf)
|
||||
return stm << pointf.x << "," << pointf.y;
|
||||
}
|
||||
|
||||
double
|
||||
Pointf::ccw(const Pointf &p1, const Pointf &p2) const
|
||||
{
|
||||
return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
|
||||
}
|
||||
|
||||
std::string
|
||||
Pointf::wkt() const
|
||||
{
|
||||
|
||||
@@ -221,6 +221,7 @@ public:
|
||||
static Pointf new_unscale(const Point &p) {
|
||||
return Pointf(unscale(p.x), unscale(p.y));
|
||||
};
|
||||
double ccw(const Pointf &p1, const Pointf &p2) const;
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
|
||||
@@ -128,7 +128,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||
"gcode_comments",
|
||||
"gcode_flavor",
|
||||
"infill_acceleration",
|
||||
"infill_first",
|
||||
"layer_gcode",
|
||||
"min_fan_speed",
|
||||
"max_fan_speed",
|
||||
@@ -155,6 +154,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||
"retract_restart_extra",
|
||||
"retract_restart_extra_toolchange",
|
||||
"retract_speed",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"standby_temperature_delta",
|
||||
"start_gcode",
|
||||
@@ -166,17 +166,16 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||
"use_relative_e_distances",
|
||||
"use_volumetric_e",
|
||||
"variable_layer_height",
|
||||
"wipe"
|
||||
"wipe",
|
||||
"wipe_tower_x",
|
||||
"wipe_tower_y",
|
||||
"wipe_tower_rotation_angle"
|
||||
};
|
||||
|
||||
std::vector<PrintStep> steps;
|
||||
std::vector<PrintObjectStep> osteps;
|
||||
bool invalidated = false;
|
||||
|
||||
// Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
|
||||
// features - nearly anything can influence what should (and could) be wiped into.
|
||||
steps.emplace_back(psWipeTower);
|
||||
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||
// These options only affect G-code export or they are just notes without influence on the generated G-code,
|
||||
@@ -201,21 +200,22 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||
|| opt_key == "filament_soluble"
|
||||
|| opt_key == "first_layer_temperature"
|
||||
|| opt_key == "filament_loading_speed"
|
||||
|| opt_key == "filament_loading_speed_start"
|
||||
|| opt_key == "filament_unloading_speed"
|
||||
|| opt_key == "filament_unloading_speed_start"
|
||||
|| opt_key == "filament_toolchange_delay"
|
||||
|| opt_key == "filament_cooling_moves"
|
||||
|| opt_key == "filament_minimal_purge_on_wipe_tower"
|
||||
|| opt_key == "filament_cooling_initial_speed"
|
||||
|| opt_key == "filament_cooling_final_speed"
|
||||
|| opt_key == "filament_ramming_parameters"
|
||||
|| opt_key == "gcode_flavor"
|
||||
|| opt_key == "infill_first"
|
||||
|| opt_key == "single_extruder_multi_material"
|
||||
|| opt_key == "spiral_vase"
|
||||
|| opt_key == "temperature"
|
||||
|| opt_key == "wipe_tower"
|
||||
|| opt_key == "wipe_tower_x"
|
||||
|| opt_key == "wipe_tower_y"
|
||||
|| opt_key == "wipe_tower_width"
|
||||
|| opt_key == "wipe_tower_rotation_angle"
|
||||
|| opt_key == "wipe_tower_bridging"
|
||||
|| opt_key == "wiping_volumes_matrix"
|
||||
|| opt_key == "parking_pos_retraction"
|
||||
@@ -1051,6 +1051,8 @@ void Print::_make_wipe_tower()
|
||||
if (! this->has_wipe_tower())
|
||||
return;
|
||||
|
||||
m_wipe_tower_depth = 0.f;
|
||||
|
||||
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
||||
std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
|
||||
// Extract purging volumes for each extruder pair:
|
||||
@@ -1123,7 +1125,9 @@ void Print::_make_wipe_tower()
|
||||
this->config.temperature.get_at(i),
|
||||
this->config.first_layer_temperature.get_at(i),
|
||||
this->config.filament_loading_speed.get_at(i),
|
||||
this->config.filament_loading_speed_start.get_at(i),
|
||||
this->config.filament_unloading_speed.get_at(i),
|
||||
this->config.filament_unloading_speed_start.get_at(i),
|
||||
this->config.filament_toolchange_delay.get_at(i),
|
||||
this->config.filament_cooling_moves.get_at(i),
|
||||
this->config.filament_cooling_initial_speed.get_at(i),
|
||||
@@ -1144,12 +1148,19 @@ void Print::_make_wipe_tower()
|
||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
|
||||
for (const auto extruder_id : layer_tools.extruders) {
|
||||
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
// Not all of that can be used for infill purging:
|
||||
volume_to_wipe -= config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
||||
// try to assign some infills/objects for the wiping:
|
||||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
|
||||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
|
||||
|
||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||
// add back the minimal amount toforce on the wipe tower:
|
||||
volume_to_wipe += config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
||||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
|
||||
first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||
current_extruder_id = extruder_id;
|
||||
}
|
||||
}
|
||||
@@ -1162,7 +1173,8 @@ void Print::_make_wipe_tower()
|
||||
// Generate the wipe tower layers.
|
||||
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
|
||||
wipe_tower.generate(m_wipe_tower_tool_changes);
|
||||
|
||||
m_wipe_tower_depth = wipe_tower.get_depth();
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
coordf_t layer_height = this->objects.front()->config.layer_height.value;
|
||||
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
|
||||
@@ -1183,10 +1195,6 @@ void Print::_make_wipe_tower()
|
||||
wipe_tower.tool_change((unsigned int)-1, false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Print::output_filename()
|
||||
{
|
||||
this->placeholder_parser.update_timestamp();
|
||||
@@ -1225,7 +1233,6 @@ void Print::set_status(int percent, const std::string &message)
|
||||
printf("Print::status %d => %s\n", percent, message.c_str());
|
||||
}
|
||||
|
||||
|
||||
// Returns extruder this eec should be printed with, according to PrintRegion config
|
||||
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion)
|
||||
{
|
||||
@@ -1233,5 +1240,4 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion
|
||||
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -276,6 +276,7 @@ public:
|
||||
|
||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||
bool apply_config(DynamicPrintConfig config);
|
||||
float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
|
||||
bool has_infinite_skirt() const;
|
||||
bool has_skirt() const;
|
||||
// Returns an empty string if valid, otherwise returns an error message.
|
||||
@@ -329,6 +330,9 @@ private:
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
||||
|
||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float m_wipe_tower_depth = 0.f;
|
||||
|
||||
// Has the calculation been canceled?
|
||||
tbb::atomic<bool> m_canceled;
|
||||
};
|
||||
|
||||
@@ -473,6 +473,14 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 28. };
|
||||
|
||||
def = this->add("filament_loading_speed_start", coFloats);
|
||||
def->label = L("Loading speed at the start");
|
||||
def->tooltip = L("Speed used at the very beginning of loading phase. ");
|
||||
def->sidetext = L("mm/s");
|
||||
def->cli = "filament-loading-speed-start=f@";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 3. };
|
||||
|
||||
def = this->add("filament_unloading_speed", coFloats);
|
||||
def->label = L("Unloading speed");
|
||||
def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
|
||||
@@ -482,6 +490,14 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 90. };
|
||||
|
||||
def = this->add("filament_unloading_speed_start", coFloats);
|
||||
def->label = L("Unloading speed at the start");
|
||||
def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
|
||||
def->sidetext = L("mm/s");
|
||||
def->cli = "filament-unloading-speed-start=f@";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 100. };
|
||||
|
||||
def = this->add("filament_toolchange_delay", coFloats);
|
||||
def->label = L("Delay after unloading");
|
||||
def->tooltip = L("Time to wait after the filament is unloaded. "
|
||||
@@ -504,19 +520,38 @@ PrintConfigDef::PrintConfigDef()
|
||||
def = this->add("filament_cooling_initial_speed", coFloats);
|
||||
def->label = L("Speed of the first cooling move");
|
||||
def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
|
||||
def->cli = "filament-cooling-initial-speed=i@";
|
||||
def->cli = "filament-cooling-initial-speed=f@";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 2.2f };
|
||||
|
||||
def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
|
||||
def->label = L("Minimal purge on wipe tower");
|
||||
def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
|
||||
"the nozzle may not be known, and the filament pressure is likely not yet stable. "
|
||||
"Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
|
||||
"this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
|
||||
def->cli = "filament-minimal-purge-on-wipe-tower=f@";
|
||||
def->sidetext = L("mm³");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 15.f };
|
||||
|
||||
def = this->add("filament_cooling_final_speed", coFloats);
|
||||
def->label = L("Speed of the last cooling move");
|
||||
def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
|
||||
def->cli = "filament-cooling-final-speed=i@";
|
||||
def->cli = "filament-cooling-final-speed=f@";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 3.4f };
|
||||
|
||||
def = this->add("filament_load_time", coFloats);
|
||||
def->label = L("Filament load time");
|
||||
def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to load a new filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
|
||||
def->cli = "filament-load-time=i@";
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 0.0f };
|
||||
|
||||
def = this->add("filament_ramming_parameters", coStrings);
|
||||
def->label = L("Ramming parameters");
|
||||
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
|
||||
@@ -524,6 +559,14 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|"
|
||||
" 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
|
||||
|
||||
def = this->add("filament_unload_time", coFloats);
|
||||
def->label = L("Filament unload time");
|
||||
def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to unload a filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
|
||||
def->cli = "filament-unload-time=i@";
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 0.0f };
|
||||
|
||||
def = this->add("filament_diameter", coFloats);
|
||||
def->label = L("Diameter");
|
||||
def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
|
||||
@@ -545,10 +588,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("filament_type", coStrings);
|
||||
def->label = L("Filament type");
|
||||
def->tooltip = L("If you want to process the output G-code through custom scripts, just list their "
|
||||
"absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed "
|
||||
"the absolute path to the G-code file as the first argument, and they can access "
|
||||
"the Slic3r config settings by reading environment variables.");
|
||||
def->tooltip = L("The filament material type for use in custom G-codes.");
|
||||
def->cli = "filament_type=s@";
|
||||
def->gui_type = "f_enum_open";
|
||||
def->gui_flags = "show_value";
|
||||
@@ -892,8 +932,16 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(0.3);
|
||||
|
||||
def = this->add("remaining_times", coBool);
|
||||
def->label = L("Supports remaining times");
|
||||
def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
|
||||
" intervals into the G-code to let the firmware show accurate remaining time."
|
||||
" As of now only the Prusa i3 MK3 firmware recognizes M73."
|
||||
" Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode.");
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("silent_mode", coBool);
|
||||
def->label = L("Support silent mode");
|
||||
def->label = L("Supports silent mode");
|
||||
def->tooltip = L("Set silent mode for the G-code flavor");
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
@@ -1105,25 +1153,37 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "nozzle-diameter=f@";
|
||||
def->default_value = new ConfigOptionFloats { 0.5 };
|
||||
|
||||
def = this->add("octoprint_apikey", coString);
|
||||
def->label = L("API Key");
|
||||
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
|
||||
"the API Key required for authentication.");
|
||||
def->cli = "octoprint-apikey=s";
|
||||
def = this->add("host_type", coEnum);
|
||||
def->label = L("Host Type");
|
||||
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
|
||||
"the kind of the host.");
|
||||
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 = this->add("octoprint_cafile", coString);
|
||||
def = this->add("printhost_cafile", coString);
|
||||
def->label = "HTTPS CA file";
|
||||
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.";
|
||||
def->cli = "octoprint-cafile=s";
|
||||
def->cli = "printhost-cafile=s";
|
||||
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->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
|
||||
"the hostname, IP address or URL of the OctoPrint instance.");
|
||||
def->cli = "octoprint-host=s";
|
||||
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 printer host instance.");
|
||||
def->cli = "print-host=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("only_retract_when_crossing_perimeters", coBool);
|
||||
@@ -1623,6 +1683,12 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "single-extruder-multi-material!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("single_extruder_multi_material_priming", coBool);
|
||||
def->label = L("Prime all printing extruders");
|
||||
def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
|
||||
def->cli = "single-extruder-multi-material-priming!";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_material", coBool);
|
||||
def->label = L("Generate support material");
|
||||
def->category = L("Support material");
|
||||
@@ -1993,8 +2059,8 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("wipe_into_infill", coBool);
|
||||
def->category = L("Extruders");
|
||||
def->label = L("Purging into infill");
|
||||
def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
|
||||
def->label = L("Wipe into this object's infill");
|
||||
def->tooltip = L("Purging after toolchange will done inside this object's infills. "
|
||||
"This lowers the amount of waste but may result in longer print time "
|
||||
" due to additional travel moves.");
|
||||
def->cli = "wipe-into-infill!";
|
||||
@@ -2002,8 +2068,8 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("wipe_into_objects", coBool);
|
||||
def->category = L("Extruders");
|
||||
def->label = L("Purging into objects");
|
||||
def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
|
||||
def->label = L("Wipe into this object");
|
||||
def->tooltip = L("Object will be used to purge the nozzle after a toolchange to save material "
|
||||
"that would otherwise end up in the wipe tower and decrease print time. "
|
||||
"Colours of the objects will be mixed as a result.");
|
||||
def->cli = "wipe-into-objects!";
|
||||
@@ -2069,10 +2135,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
std::ostringstream oss;
|
||||
oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y;
|
||||
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")
|
||||
|| (opt_key == "infill_acceleration" && value == "50")) {
|
||||
/* For historical reasons, the world's full of configs having these very low values;
|
||||
@@ -2083,6 +2145,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
} else if (opt_key == "support_material_pattern" && value == "pillars") {
|
||||
// Slic3r PE does not support the pillars. They never worked well.
|
||||
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:
|
||||
@@ -2092,9 +2160,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"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"
|
||||
};
|
||||
|
||||
@@ -2104,7 +2169,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
}
|
||||
|
||||
if (! print_config_def.has(opt_key)) {
|
||||
//printf("Unknown option %s\n", opt_key.c_str());
|
||||
opt_key = "";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ enum GCodeFlavor {
|
||||
gcfSmoothie, gcfNoExtrusion,
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htOctoPrint, htDuet,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||
@@ -61,6 +65,15 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
|
||||
return keys_map;
|
||||
}
|
||||
|
||||
template<> inline 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 t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
|
||||
static t_config_enum_values keys_map;
|
||||
if (keys_map.empty()) {
|
||||
@@ -536,10 +549,15 @@ public:
|
||||
ConfigOptionFloats filament_cost;
|
||||
ConfigOptionFloats filament_max_volumetric_speed;
|
||||
ConfigOptionFloats filament_loading_speed;
|
||||
ConfigOptionFloats filament_loading_speed_start;
|
||||
ConfigOptionFloats filament_load_time;
|
||||
ConfigOptionFloats filament_unloading_speed;
|
||||
ConfigOptionFloats filament_unloading_speed_start;
|
||||
ConfigOptionFloats filament_toolchange_delay;
|
||||
ConfigOptionFloats filament_unload_time;
|
||||
ConfigOptionInts filament_cooling_moves;
|
||||
ConfigOptionFloats filament_cooling_initial_speed;
|
||||
ConfigOptionFloats filament_minimal_purge_on_wipe_tower;
|
||||
ConfigOptionFloats filament_cooling_final_speed;
|
||||
ConfigOptionStrings filament_ramming_parameters;
|
||||
ConfigOptionBool gcode_comments;
|
||||
@@ -561,6 +579,7 @@ public:
|
||||
ConfigOptionString start_gcode;
|
||||
ConfigOptionStrings start_filament_gcode;
|
||||
ConfigOptionBool single_extruder_multi_material;
|
||||
ConfigOptionBool single_extruder_multi_material_priming;
|
||||
ConfigOptionString toolchange_gcode;
|
||||
ConfigOptionFloat travel_speed;
|
||||
ConfigOptionBool use_firmware_retraction;
|
||||
@@ -570,6 +589,7 @@ public:
|
||||
ConfigOptionFloat cooling_tube_retraction;
|
||||
ConfigOptionFloat cooling_tube_length;
|
||||
ConfigOptionFloat parking_pos_retraction;
|
||||
ConfigOptionBool remaining_times;
|
||||
ConfigOptionBool silent_mode;
|
||||
ConfigOptionFloat extra_loading_move;
|
||||
|
||||
@@ -597,10 +617,15 @@ protected:
|
||||
OPT_PTR(filament_cost);
|
||||
OPT_PTR(filament_max_volumetric_speed);
|
||||
OPT_PTR(filament_loading_speed);
|
||||
OPT_PTR(filament_loading_speed_start);
|
||||
OPT_PTR(filament_load_time);
|
||||
OPT_PTR(filament_unloading_speed);
|
||||
OPT_PTR(filament_unloading_speed_start);
|
||||
OPT_PTR(filament_unload_time);
|
||||
OPT_PTR(filament_toolchange_delay);
|
||||
OPT_PTR(filament_cooling_moves);
|
||||
OPT_PTR(filament_cooling_initial_speed);
|
||||
OPT_PTR(filament_minimal_purge_on_wipe_tower);
|
||||
OPT_PTR(filament_cooling_final_speed);
|
||||
OPT_PTR(filament_ramming_parameters);
|
||||
OPT_PTR(gcode_comments);
|
||||
@@ -620,6 +645,7 @@ protected:
|
||||
OPT_PTR(retract_restart_extra_toolchange);
|
||||
OPT_PTR(retract_speed);
|
||||
OPT_PTR(single_extruder_multi_material);
|
||||
OPT_PTR(single_extruder_multi_material_priming);
|
||||
OPT_PTR(start_gcode);
|
||||
OPT_PTR(start_filament_gcode);
|
||||
OPT_PTR(toolchange_gcode);
|
||||
@@ -631,6 +657,7 @@ protected:
|
||||
OPT_PTR(cooling_tube_retraction);
|
||||
OPT_PTR(cooling_tube_length);
|
||||
OPT_PTR(parking_pos_retraction);
|
||||
OPT_PTR(remaining_times);
|
||||
OPT_PTR(silent_mode);
|
||||
OPT_PTR(extra_loading_move);
|
||||
}
|
||||
@@ -787,18 +814,20 @@ class HostConfig : public StaticPrintConfig
|
||||
{
|
||||
STATIC_PRINT_CONFIG_CACHE(HostConfig)
|
||||
public:
|
||||
ConfigOptionString octoprint_host;
|
||||
ConfigOptionString octoprint_apikey;
|
||||
ConfigOptionString octoprint_cafile;
|
||||
ConfigOptionEnum<PrintHostType> host_type;
|
||||
ConfigOptionString print_host;
|
||||
ConfigOptionString printhost_apikey;
|
||||
ConfigOptionString printhost_cafile;
|
||||
ConfigOptionString serial_port;
|
||||
ConfigOptionInt serial_speed;
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(octoprint_host);
|
||||
OPT_PTR(octoprint_apikey);
|
||||
OPT_PTR(octoprint_cafile);
|
||||
OPT_PTR(host_type);
|
||||
OPT_PTR(print_host);
|
||||
OPT_PTR(printhost_apikey);
|
||||
OPT_PTR(printhost_cafile);
|
||||
OPT_PTR(serial_port);
|
||||
OPT_PTR(serial_speed);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ bool PrintObject::delete_last_copy()
|
||||
|
||||
bool PrintObject::set_copies(const Points &points)
|
||||
{
|
||||
bool copies_num_changed = this->_copies.size() != points.size();
|
||||
this->_copies = points;
|
||||
|
||||
// order copies with a nearest neighbor search and translate them by _copies_shift
|
||||
@@ -93,7 +94,8 @@ bool PrintObject::set_copies(const Points &points)
|
||||
|
||||
bool invalidated = this->_print->invalidate_step(psSkirt);
|
||||
invalidated |= this->_print->invalidate_step(psBrim);
|
||||
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||
if (copies_num_changed)
|
||||
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "ClipperUtils.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 <deque>
|
||||
#include <queue>
|
||||
@@ -10,11 +13,14 @@
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#if 0
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
@@ -318,6 +324,17 @@ void TriangleMesh::translate(float x, float y, float z)
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, Pointf3 axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
return;
|
||||
|
||||
axis = normalize(axis);
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
|
||||
m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z)));
|
||||
stl_transform(&stl, (float*)m.data());
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
@@ -597,6 +614,140 @@ TriangleMesh::bounding_box() const
|
||||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector<float>& matrix) const
|
||||
{
|
||||
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::MatrixXf 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) = vertex_ptr->x;
|
||||
src_vertices(1, i) = vertex_ptr->y;
|
||||
src_vertices(2, i) = vertex_ptr->z;
|
||||
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) = facet_ptr->vertex[i].x;
|
||||
src_vertices(1, v_id) = facet_ptr->vertex[i].y;
|
||||
src_vertices(2, v_id) = facet_ptr->vertex[i].z;
|
||||
}
|
||||
facet_ptr += 1;
|
||||
++v_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_shared && (stl.stats.shared_vertices > 0))
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m;
|
||||
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
|
||||
|
||||
Eigen::MatrixXf dst_vertices(3, vertices_count);
|
||||
dst_vertices = m * src_vertices.colwise().homogeneous();
|
||||
|
||||
float min_x = dst_vertices(0, 0);
|
||||
float max_x = dst_vertices(0, 0);
|
||||
float min_y = dst_vertices(1, 0);
|
||||
float max_y = dst_vertices(1, 0);
|
||||
float min_z = dst_vertices(2, 0);
|
||||
float max_z = dst_vertices(2, 0);
|
||||
|
||||
for (int i = 1; i < vertices_count; ++i)
|
||||
{
|
||||
min_x = std::min(min_x, dst_vertices(0, i));
|
||||
max_x = std::max(max_x, dst_vertices(0, i));
|
||||
min_y = std::min(min_y, dst_vertices(1, i));
|
||||
max_y = std::max(max_y, dst_vertices(1, i));
|
||||
min_z = std::min(min_z, dst_vertices(2, i));
|
||||
max_z = std::max(max_z, dst_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));
|
||||
}
|
||||
|
||||
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.x, v.y, v.z);
|
||||
}
|
||||
|
||||
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 det_vertices;
|
||||
std::vector<Point3> 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();
|
||||
det_vertices.emplace_back(coords[0], coords[1], coords[2]);
|
||||
}
|
||||
unsigned int size = (unsigned int)det_vertices.size();
|
||||
facets.emplace_back(size - 3, size - 2, size - 1);
|
||||
}
|
||||
|
||||
TriangleMesh output_mesh(det_vertices, facets);
|
||||
output_mesh.repair();
|
||||
output_mesh.require_shared_vertices();
|
||||
return output_mesh;
|
||||
}
|
||||
|
||||
const float* TriangleMesh::first_vertex() const
|
||||
{
|
||||
return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::require_shared_vertices()
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
void scale(const Pointf3 &versor);
|
||||
void translate(float x, float y, float z);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void rotate(float angle, Pointf3 axis);
|
||||
void rotate_x(float angle);
|
||||
void rotate_y(float angle);
|
||||
void rotate_z(float angle);
|
||||
@@ -53,8 +54,13 @@ public:
|
||||
TriangleMeshPtrs split() const;
|
||||
void merge(const TriangleMesh &mesh);
|
||||
ExPolygons horizontal_projection() const;
|
||||
const float* first_vertex() const;
|
||||
Polygon convex_hull();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given matrix
|
||||
BoundingBoxf3 transformed_bounding_box(const std::vector<float>& matrix) const;
|
||||
// Returns the convex hull of this TriangleMesh
|
||||
TriangleMesh convex_hull_3d() const;
|
||||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
size_t facets_count() const;
|
||||
@@ -66,7 +72,7 @@ public:
|
||||
// Count disconnected triangle patches.
|
||||
size_t number_of_patches() const;
|
||||
|
||||
stl_file stl;
|
||||
mutable stl_file stl;
|
||||
bool repaired;
|
||||
|
||||
private:
|
||||
|
||||
@@ -84,6 +84,8 @@ inline T next_highest_power_of_2(T v)
|
||||
return ++ v;
|
||||
}
|
||||
|
||||
extern std::string xml_escape(std::string text);
|
||||
|
||||
class PerlCallback {
|
||||
public:
|
||||
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.41.0-alpha2"
|
||||
#define SLIC3R_VERSION "1.41.0-beta2"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef int32_t coord_t;
|
||||
|
||||
@@ -387,4 +387,31 @@ unsigned get_current_pid()
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string xml_escape(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
pos = text.find_first_of("\"\'&<>", pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string replacement;
|
||||
switch (text[pos])
|
||||
{
|
||||
case '\"': replacement = """; break;
|
||||
case '\'': replacement = "'"; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = "<"; break;
|
||||
case '>': replacement = ">"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text.replace(pos, 1, replacement);
|
||||
pos += replacement.size();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
@@ -64,9 +64,9 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||
REGISTER_CLASS(TabIface, "GUI::Tab");
|
||||
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
|
||||
REGISTER_CLASS(OctoPrint, "OctoPrint");
|
||||
REGISTER_CLASS(AppController, "AppController");
|
||||
REGISTER_CLASS(PrintController, "PrintController");
|
||||
REGISTER_CLASS(PrintHost, "PrintHost");
|
||||
|
||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||
{
|
||||
|
||||
47
xs/src/qhull/Announce.txt
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <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 © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><a href="#TOP">»</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 • <a
|
||||
href="#input">in</a>put • <a href="#output">ou</a>tput
|
||||
• <a href="#algorithm">al</a>gorithm • <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">»</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">»</a><a name="description">Description of
|
||||
Qhull </a></h2>
|
||||
<blockquote>
|
||||
|
||||
<h3><a href="#TOC">»</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 <= [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
|
||||
"thick" 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">»</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">»</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">»</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">»</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 & Shamos <a
|
||||
href="#pre-sha85">'85</a>]. It is similar to the randomized
|
||||
algorithms of Clarkson and others [Clarkson & 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">»</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 <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">»</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">»</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">»</a>Options for using Qhull </h2>
|
||||
<blockquote>
|
||||
|
||||
<p>See <a href="qh-quick.htm#options">Options</a>.</p>
|
||||
|
||||
</blockquote>
|
||||
<h2><a href="#TOC">»</a>Qhull internals </h2>
|
||||
<blockquote>
|
||||
|
||||
<p>See <a href="qh-code.htm">Internals</a>.</p>
|
||||
|
||||
</blockquote>
|
||||
<h2><a href="#TOC">»</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">»</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">»</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">»</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">»</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 "internal error". 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">»</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">»</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">»</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">»</a><a name="ref">References</a></h2>
|
||||
<blockquote>
|
||||
|
||||
<p><a name="aure91">Aurenhammer</a>, F., "Voronoi diagrams
|
||||
-- A survey of a fundamental geometric data structure," <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, "The Quickhull Algorithm for Convex Hulls," <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,
|
||||
"Applications of random sampling in computational geometry,
|
||||
II", <i>Discrete Computational Geometry</i>, 4:387-421, 1989</p>
|
||||
|
||||
<p><a name="cla-meh93">Clarkson</a>, K.L., K. Mehlhorn, and R.
|
||||
Seidel, "Four results on randomized incremental
|
||||
construction," <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,
|
||||
"Determining the separation of preprocessed polyhedra--a
|
||||
unified approach," 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>, "Computational
|
||||
geometry," 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>, "Robust polygon
|
||||
modeling," 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <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>
|
||||
BIN
xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
630
xs/src/qhull/html/qconvex.htm
Normal 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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> </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> </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> </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> </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> </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> </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> </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> </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> </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 © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#TOP">»</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">»</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 < 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 > 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 < 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">»</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> </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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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">»</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">»</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">»</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">»</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">»</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] <= 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>
|
||||
•<a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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>
|
||||
416
xs/src/qhull/html/qdelau_f.htm
Normal 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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> </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> </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
|
||||
"vertical" 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 © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#TOP">»</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">»</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 < 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 > 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 < 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">»</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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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">»</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">»</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">»</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">»</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">»</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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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>
|
||||
628
xs/src/qhull/html/qdelaun.htm
Normal 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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> </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> </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 © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#TOP">»</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">»</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 < 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 > 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 < 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">»</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> </dt>
|
||||
<dt> </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> </dt>
|
||||
<dt> </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">»</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">»</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">»</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 <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">»</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">»</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] <= 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#synopsis">sy</a>nopsis
|
||||
• <a href="#input">in</a>put • <a href="#outputs">ou</a>tputs
|
||||
• <a href="#controls">co</a>ntrols • <a href="#graphics">gr</a>aphics
|
||||
• <a href="#notes">no</a>tes • <a href="#conventions">co</a>nventions
|
||||
• <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>
|
||||
BIN
xs/src/qhull/html/qh--4d.gif
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
xs/src/qhull/html/qh--cone.gif
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
xs/src/qhull/html/qh--dt.gif
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
xs/src/qhull/html/qh--geom.gif
Normal file
|
After Width: | Height: | Size: 318 B |
BIN
xs/src/qhull/html/qh--half.gif
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
xs/src/qhull/html/qh--rand.gif
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
1062
xs/src/qhull/html/qh-code.htm
Normal file
693
xs/src/qhull/html/qh-eg.htm
Normal file
@@ -0,0 +1,693 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Examples of Qhull</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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="index.htm#TOC">Qhull manual</a>: Table of Contents<br>
|
||||
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To: </b><a href="#TOC">Qhull examples: Table of Contents</a> (please wait
|
||||
while loading)<br>
|
||||
|
||||
<hr>
|
||||
<!-- Main text of document -->
|
||||
<h1><a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html"><img
|
||||
src="qh--half.gif" alt="[halfspace]" align="middle" width="100"
|
||||
height="100"></a> Examples of Qhull</h1>
|
||||
|
||||
<p>This section of the Qhull manual will introduce you to Qhull
|
||||
and its options. Each example is a file for viewing with <a
|
||||
href="index.htm#geomview">Geomview</a>. You will need to
|
||||
use a Unix computer with a copy of Geomview.
|
||||
<p>
|
||||
If you are not running Unix, you can view <a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/welcome.html">pictures</a>
|
||||
for some of the examples. To understand Qhull without Geomview, try the
|
||||
examples in <a href="qh-quick.htm#programs">Programs</a> and
|
||||
<a href="qh-quick.htm#programs">Programs/input</a>. You can also try small
|
||||
examples that you compute by hand. Use <a href="rbox.htm">rbox</a>
|
||||
to generate examples.
|
||||
<p>
|
||||
To generate the Geomview examples, execute the shell script <tt>eg/q_eg</tt>.
|
||||
It uses <tt>rbox</tt>. The shell script <tt>eg/q_egtest</tt> generates
|
||||
test examples, and <tt>eg/q_test</tt> exercises the code. If you
|
||||
find yourself viewing the inside of a 3-d example, use Geomview's
|
||||
normalization option on the 'obscure' menu.</p>
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><a href="#TOP">»</a><a name="TOC">Qhull examples: Table of
|
||||
Contents </a></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="#2d">2-d and 3-d examples</a></li>
|
||||
<li><a href="#how">How Qhull adds a point</a></li>
|
||||
<li><a href="#joggle">Triangulated output or joggled input</a></li>
|
||||
<li><a href="#delaunay">Delaunay and Voronoi diagrams</a></li>
|
||||
<li><a href="#merge">Facet merging for imprecision</a></li>
|
||||
<li><a href="#4d">4-d objects</a></li>
|
||||
<li><a href="#half">Halfspace intersections</a></li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#TOC">»</a><a name="2d">2-d and 3-d examples</a><ul>
|
||||
<li><a href="#01">eg.01.cube</a></li>
|
||||
<li><a href="#02">eg.02.diamond.cube</a></li>
|
||||
<li><a href="#03">eg.03.sphere</a></li>
|
||||
<li><a href="#04">eg.04.circle</a></li>
|
||||
<li><a href="#05">eg.05.spiral</a></li>
|
||||
<li><a href="#06">eg.06.merge.square</a></li>
|
||||
<li><a href="#07">eg.07.box</a></li>
|
||||
<li><a href="#08a">eg.08a.cube.sphere</a></li>
|
||||
<li><a href="#08b">eg.08b.diamond.sphere</a></li>
|
||||
<li><a href="#09">eg.09.lens</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#TOC">»</a><a name="how">How Qhull adds a point</a><ul>
|
||||
<li><a href="#10a">eg.10a.sphere.visible</a></li>
|
||||
<li><a href="#10b">eg.10b.sphere.beyond</a></li>
|
||||
<li><a href="#10c">eg.10c.sphere.horizon</a></li>
|
||||
<li><a href="#10d">eg.10d.sphere.cone</a></li>
|
||||
<li><a href="#10e">eg.10e.sphere.new</a></li>
|
||||
<li><a href="#14">eg.14.sphere.corner</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#TOC">»</a> <a name="joggle">Triangulated output or joggled input</a>
|
||||
<ul>
|
||||
<li><a href="#15a">eg.15a.surface</a></li>
|
||||
<li><a href="#15b">eg.15b.triangle</a></li>
|
||||
<li><a href="#15c">eg.15c.joggle</a></li>
|
||||
</ul>
|
||||
<li><a href="#TOC">»</a><a name="delaunay"> Delaunay and
|
||||
Voronoi diagrams</a><ul>
|
||||
<li><a href="#17a">eg.17a.delaunay.2</a></li>
|
||||
<li><a href="#17b">eg.17b.delaunay.2i</a></li>
|
||||
<li><a href="#17c">eg.17c.delaunay.2-3</a></li>
|
||||
<li><a href="#17d">eg.17d.voronoi.2</a></li>
|
||||
<li><a href="#17e">eg.17e.voronoi.2i</a></li>
|
||||
<li><a href="#17f">eg.17f.delaunay.3</a></li>
|
||||
<li><a href="#18a">eg.18a.furthest.2-3</a></li>
|
||||
<li><a href="#18b">eg.18b.furthest-up.2-3</a></li>
|
||||
<li><a href="#18c">eg.18c.furthest.2</a></li>
|
||||
<li><a href="#19">eg.19.voronoi.region.3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#TOC">»</a><a name="merge">Facet merging for
|
||||
imprecision </a><ul>
|
||||
<li><a href="#20">eg.20.cone</a></li>
|
||||
<li><a href="#21a">eg.21a.roundoff.errors</a></li>
|
||||
<li><a href="#21b">eg.21b.roundoff.fixed</a></li>
|
||||
<li><a href="#22a">eg.22a.merge.sphere.01</a></li>
|
||||
<li><a href="#22b">eg.22b.merge.sphere.-01</a></li>
|
||||
<li><a href="#22c">eg.22c.merge.sphere.05</a></li>
|
||||
<li><a href="#22d">eg.22d.merge.sphere.-05</a></li>
|
||||
<li><a href="#23">eg.23.merge.cube</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#TOC">»</a><a name="4d">4-d objects</a><ul>
|
||||
<li><a href="#24">eg.24.merge.cube.4d-in-3d</a></li>
|
||||
<li><a href="#30">eg.30.4d.merge.cube</a></li>
|
||||
<li><a href="#31">eg.31.4d.delaunay</a></li>
|
||||
<li><a href="#32">eg.32.4d.octant</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#TOC">»</a><a name="half">Halfspace
|
||||
intersections</a><ul>
|
||||
<li><a href="#33a">eg.33a.cone</a></li>
|
||||
<li><a href="#33b">eg.33b.cone.dual</a></li>
|
||||
<li><a href="#33c">eg.33c.cone.halfspace</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><a href="#TOC">»</a>2-d and 3-d examples</h2>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="01">rbox c D3 | qconvex G
|
||||
>eg.01.cube </a></h3>
|
||||
|
||||
<p>The first example is a cube in 3-d. The color of each facet
|
||||
indicates its normal. For example, normal [0,0,1] along the Z
|
||||
axis is (r=0.5, g=0.5, b=1.0). With the 'Dn' option in <tt>rbox</tt>,
|
||||
you can generate hypercubes in any dimension. Above 7-d the
|
||||
number of intermediate facets grows rapidly. Use '<a
|
||||
href="qh-optt.htm#TFn">TFn</a>' to track qconvex's progress. Note
|
||||
that each facet is a square that qconvex merged from coplanar
|
||||
triangles.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="02">rbox c d G3.0 | qconvex G
|
||||
>eg.02.diamond.cube </a></h3>
|
||||
|
||||
<p>The second example is a cube plus a diamond ('d') scaled by <tt>rbox</tt>'s
|
||||
'G' option. In higher dimensions, diamonds are much simpler than
|
||||
hypercubes. </p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="03">rbox s 100 D3 | qconvex G
|
||||
>eg.03.sphere </a></h3>
|
||||
|
||||
<p>The <tt>rbox s</tt> option generates random points and
|
||||
projects them to the d-sphere. All points should be on the convex
|
||||
hull. Notice that random points look more clustered than you
|
||||
might expect. You can get a smoother distribution by merging
|
||||
facets and printing the vertices, e.g.,<i> rbox 1000 s | qconvex
|
||||
A-0.95 p | qconvex G >eg.99</i>.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="04">rbox s 100 D2 | qconvex G
|
||||
>eg.04.circle </a></h3>
|
||||
|
||||
<p>In 2-d, there are many ways to generate a convex hull. One of
|
||||
the earliest algorithms, and one of the fastest, is the 2-d
|
||||
Quickhull algorithm [c.f., Preparata & Shamos <a
|
||||
href="index.htm#pre-sha85">'85</a>]. It was the model for
|
||||
Qhull.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="05">rbox 10 l | qconvex G
|
||||
>eg.05.spiral </a></h3>
|
||||
|
||||
<p>One rotation of a spiral.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="06">rbox 1000 D2 | qconvex C-0.03
|
||||
Qc Gapcv >eg.06.merge.square</a></h3>
|
||||
|
||||
<p>This demonstrates how Qhull handles precision errors. Option '<a
|
||||
href="qh-optc.htm#Cn">C-0.03</a>' requires a clearly convex angle
|
||||
between adjacent facets. Otherwise, Qhull merges the facets. </p>
|
||||
|
||||
<p>This is the convex hull of random points in a square. The
|
||||
facets have thickness because they must be outside all points and
|
||||
must include their vertices. The colored lines represent the
|
||||
original points and the spheres represent the vertices. Floating
|
||||
in the middle of each facet is the centrum. Each centrum is at
|
||||
least 0.03 below the planes of its neighbors. This guarantees
|
||||
that the facets are convex.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="07">rbox 1000 D3 | qconvex G
|
||||
>eg.07.box </a></h3>
|
||||
|
||||
<p>Here's the same distribution but in 3-d with Qhull handling
|
||||
machine roundoff errors. Note the large number of facets. </p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="08a">rbox c G0.4 s 500 | qconvex G
|
||||
>eg.08a.cube.sphere </a></h3>
|
||||
|
||||
<p>The sphere is just barely poking out of the cube. Try the same
|
||||
distribution with randomization turned on ('<a
|
||||
href="qh-optq.htm#Qr">Qr</a>'). This turns Qhull into a
|
||||
randomized incremental algorithm. To compare Qhull and
|
||||
randomization, look at the number of hyperplanes created and the
|
||||
number of points partitioned. Don't compare CPU times since Qhull's
|
||||
implementation of randomization is inefficient. The number of
|
||||
hyperplanes and partitionings indicate the dominant costs for
|
||||
Qhull. With randomization, you'll notice that the number of
|
||||
facets created is larger than before. This is especially true as
|
||||
you increase the number of points. It is because the randomized
|
||||
algorithm builds most of the sphere before it adds the cube's
|
||||
vertices.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="08b">rbox d G0.6 s 500 | qconvex G
|
||||
>eg.08b.diamond.sphere </a></h3>
|
||||
|
||||
<p>This is a combination of the diamond distribution and the
|
||||
sphere.</p>
|
||||
|
||||
<h3><a href="#2d">»</a><a name="09">rbox 100 L3 G0.5 s | qconvex
|
||||
G >eg.09.lens </a></h3>
|
||||
|
||||
<p>Each half of the lens distribution lies on a sphere of radius
|
||||
three. A directed search for the furthest facet below a point
|
||||
(e.g., qh_findbest in <tt>geom.c</tt>) may fail if started from
|
||||
an arbitrary facet. For example, if the first facet is on the
|
||||
opposite side of the lens, a directed search will report that the
|
||||
point is inside the convex hull even though it is outside. This
|
||||
problem occurs whenever the curvature of the convex hull is less
|
||||
than a sphere centered at the test point. </p>
|
||||
|
||||
<p>To prevent this problem, Qhull does not use directed search
|
||||
all the time. When Qhull processes a point on the edge of the
|
||||
lens, it partitions the remaining points with an exhaustive
|
||||
search instead of a directed search (see qh_findbestnew in <tt>geom2.c</tt>).
|
||||
</p>
|
||||
|
||||
<h2><a href="#TOC">»</a>How Qhull adds a point</h2>
|
||||
|
||||
<h3><a href="#how">»</a><a name="10a">rbox 100 s P0.5,0.5,0.5 |
|
||||
qconvex Ga QG0 >eg.10a.sphere.visible</a></h3>
|
||||
|
||||
<p>The next 4 examples show how Qhull adds a point. The point
|
||||
[0.5,0.5,0.5] is at one corner of the bounding box. Qhull adds a
|
||||
point using the beneath-beyond algorithm. First Qhull finds all
|
||||
of the facets that are visible from the point. Qhull will replace
|
||||
these facets with new facets.</p>
|
||||
|
||||
<h3><a href="#how">»</a><a name="10b">rbox 100 s
|
||||
P0.5,0.5,0.5|qconvex Ga QG-0 >eg.10b.sphere.beyond </a></h3>
|
||||
|
||||
<p>These are the facets that are not visible from the point.
|
||||
Qhull will keep these facets.</p>
|
||||
|
||||
<h3><a href="#how">»</a><a name="10c">rbox 100 s P0.5,0.5,0.5 |
|
||||
qconvex PG Ga QG0 >eg.10c.sphere.horizon </a></h3>
|
||||
|
||||
<p>These facets are the horizon facets; they border the visible
|
||||
facets. The inside edges are the horizon ridges. Each horizon
|
||||
ridge will form the base for a new facet.</p>
|
||||
|
||||
<h3><a href="#how">»</a><a name="10d">rbox 100 s P0.5,0.5,0.5 |
|
||||
qconvex Ga QV0 PgG >eg.10d.sphere.cone </a></h3>
|
||||
|
||||
<p>This is the cone of points from the new point to the horizon
|
||||
facets. Try combining this image with <tt>eg.10c.sphere.horizon</tt>
|
||||
and <tt>eg.10a.sphere.visible</tt>.
|
||||
</p>
|
||||
|
||||
<h3><a href="#how">»</a><a name="10e">rbox 100 s P0.5,0.5,0.5 |
|
||||
qconvex Ga >eg.10e.sphere.new</a></h3>
|
||||
|
||||
<p>This is the convex hull after [0.5,0.5,0.5] has been added.
|
||||
Note that in actual practice, the above sequence would never
|
||||
happen. Unlike the randomized algorithms, Qhull always processes
|
||||
a point that is furthest in an outside set. A point like
|
||||
[0.5,0.5,0.5] would be one of the first points processed.</p>
|
||||
|
||||
<h3><a href="#how">»</a><a name="14">rbox 100 s P0.5,0.5,0.5 |
|
||||
qhull Ga QV0g Q0 >eg.14.sphere.corner</a></h3>
|
||||
|
||||
<p>The '<a href="qh-optq.htm#QVn">QVn</a>', '<a
|
||||
href="qh-optq.htm#QGn">QGn </a>' and '<a href="qh-optp.htm#Pdk">Pdk</a>'
|
||||
options define good facets for Qhull. In this case '<a
|
||||
href="qh-optq.htm#QVn">QV0</a>' defines the 0'th point
|
||||
[0.5,0.5,0.5] as the good vertex, and '<a href="qh-optq.htm#Qg">Qg</a>'
|
||||
tells Qhull to only build facets that might be part of a good
|
||||
facet. This technique reduces output size in low dimensions. It
|
||||
does not work with facet merging.</p>
|
||||
|
||||
<h2><a href="#TOC">»</a>Triangulated output or joggled input</h2>
|
||||
|
||||
<h3><a href="#joggle">»</a><a name="15a">rbox 500 W0 | qconvex QR0 Qc Gvp >eg.15a.surface</a></h3>
|
||||
|
||||
<p>This is the convex hull of 500 points on the surface of
|
||||
a cube. Note the large, non-simplicial facet for each face.
|
||||
Qhull merges non-convex facets.
|
||||
|
||||
<p>If the facets were not merged, Qhull
|
||||
would report precision problems. For example, turn off facet merging
|
||||
with option '<a href="qh-optq.htm#Q0">Q0</a>'. Qhull may report concave
|
||||
facets, flipped facets, or other precision errors:
|
||||
<blockquote>
|
||||
rbox 500 W0 | qhull QR0 Q0
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
<h3><a href="#joggle">»</a><a name="15b">rbox 500 W0 | qconvex QR0 Qt Qc Gvp >eg.15b.triangle</a></h3>
|
||||
|
||||
<p>Like the previous examples, this is the convex hull of 500 points on the
|
||||
surface of a cube. Option '<a href="qh-optq.htm#Qt">Qt</a>' triangulates the
|
||||
non-simplicial facets. Triangulated output is
|
||||
particularly helpful for Delaunay triangulations.
|
||||
|
||||
<p>
|
||||
<h3><a href="#joggle">»</a><a name="15c">rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp >eg.15c.joggle</a></h3>
|
||||
|
||||
<p>This is the convex hull of 500 joggled points on the surface of
|
||||
a cube. The option '<a href="qh-optq.htm#QJn">QJ5e-2</a>'
|
||||
sets a very large joggle to make the effect visible. Notice
|
||||
that all of the facets are triangles. If you rotate the cube,
|
||||
you'll see red-yellow lines for coplanar points.
|
||||
<p>
|
||||
With option '<a href="qh-optq.htm#QJn">QJ</a>', Qhull joggles the
|
||||
input to avoid precision problems. It adds a small random number
|
||||
to each input coordinate. If a precision
|
||||
error occurs, it increases the joggle and tries again. It repeats
|
||||
this process until no precision problems occur.
|
||||
<p>
|
||||
Joggled input is a simple solution to precision problems in
|
||||
computational geometry. Qhull can also merge facets to handle
|
||||
precision problems. See <a href="qh-impre.htm#joggle">Merged facets or joggled input</a>.
|
||||
|
||||
<h2><a href="#TOC">»</a>Delaunay and Voronoi diagrams</h2>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17a">qdelaunay Qt
|
||||
<eg.data.17 GnraD2 >eg.17a.delaunay.2</a></h3>
|
||||
|
||||
<p>
|
||||
The input file, <tt>eg.data.17</tt>, consists of a square, 15 random
|
||||
points within the outside half of the square, and 6 co-circular
|
||||
points centered on the square.
|
||||
|
||||
<p>The Delaunay triangulation is the triangulation with empty
|
||||
circumcircles. The input for this example is unusual because it
|
||||
includes six co-circular points. Every triangular subset of these
|
||||
points has the same circumcircle. Option '<a href="qh-optq.htm#Qt">Qt</a>'
|
||||
triangulates the co-circular facet.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17b">qdelaunay <eg.data.17
|
||||
GnraD2 >eg.17b.delaunay.2i</a></h3>
|
||||
|
||||
<p>This is the same example without triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>'). qdelaunay
|
||||
merges the non-unique Delaunay triangles into a hexagon.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17c">qdelaunay <eg.data.17
|
||||
Ga >eg.17c.delaunay.2-3 </a></h3>
|
||||
|
||||
<p>This is how Qhull generated both diagrams. Use Geomview's
|
||||
'obscure' menu to turn off normalization, and Geomview's
|
||||
'cameras' menu to turn off perspective. Then load this <a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html">object</a>
|
||||
with one of the previous diagrams.</p>
|
||||
|
||||
<p>The points are lifted to a paraboloid by summing the squares
|
||||
of each coordinate. These are the light blue points. Then the
|
||||
convex hull is taken. That's what you see here. If you look up
|
||||
the Z-axis, you'll see that points and edges coincide.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17d">qvoronoi QJ
|
||||
<eg.data.17 Gna >eg.17d.voronoi.2</a></h3>
|
||||
|
||||
<p>The Voronoi diagram is the dual of the Delaunay triangulation.
|
||||
Here you see the original sites and the Voronoi vertices.
|
||||
Notice the each
|
||||
vertex is equidistant from three sites. The edges indicate the
|
||||
Voronoi region for a site. Qhull does not draw the unbounded
|
||||
edges. Instead, it draws extra edges to close the unbounded
|
||||
Voronoi regions. You may find it helpful to enclose the input
|
||||
points in a square. You can compute the unbounded
|
||||
rays from option '<a href="qh-optf.htm#Fo2">Fo</a>'.
|
||||
</p>
|
||||
|
||||
<p>Instead
|
||||
of triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>'), this
|
||||
example uses joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
|
||||
Normally, you should use neither 'QJ' nor 'Qt' for Voronoi diagrams.
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17e">qvoronoi <eg.data.17
|
||||
Gna >eg.17e.voronoi.2i </a></h3>
|
||||
|
||||
<p>This looks the same as the previous diagrams, but take a look
|
||||
at the data. Run 'qvoronoi p <eg/eg.data.17'. This prints
|
||||
the Voronoi vertices.
|
||||
|
||||
<p>With 'QJ', there are four nearly identical Voronoi vertices
|
||||
within 10^-11 of the origin. Option 'QJ' joggled the input. After the joggle,
|
||||
the cocircular
|
||||
input sites are no longer cocircular. The corresponding Voronoi vertices are
|
||||
similar but not identical.
|
||||
|
||||
<p>This example does not use options 'Qt' or 'QJ'. The cocircular
|
||||
input sites define one Voronoi vertex near the origin. </p>
|
||||
|
||||
<p>Option 'Qt' would triangulate the corresponding Delaunay region into
|
||||
four triangles. Each triangle is assigned the same Voronoi vertex.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="17f"> rbox c G0.1 d |
|
||||
qdelaunay Gt Qz <eg.17f.delaunay.3 </a></h3>
|
||||
|
||||
<p>This is the 3-d Delaunay triangulation of a small cube inside
|
||||
a prism. Since the outside ridges are transparent, it shows the
|
||||
interior of the outermost facets. If you slice open the
|
||||
triangulation with Geomview's ginsu, you will see that the innermost
|
||||
facet is a cube. Note the use of '<a href="qh-optq.htm#Qz">Qz</a>'
|
||||
to add a point "at infinity". This avoids a degenerate
|
||||
input due to cospherical points.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="18a">rbox 10 D2 d | qdelaunay
|
||||
Qu G >eg.18a.furthest.2-3 </a></h3>
|
||||
|
||||
<p>The furthest-site Voronoi diagram contains Voronoi regions for
|
||||
points that are <i>furthest </i>from an input site. It is the
|
||||
dual of the furthest-site Delaunay triangulation. You can
|
||||
determine the furthest-site Delaunay triangulation from the
|
||||
convex hull of the lifted points (<a href="#17c">eg.17c.delaunay.2-3</a>).
|
||||
The upper convex hull (blue) generates the furthest-site Delaunay
|
||||
triangulation. </p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="18b">rbox 10 D2 d | qdelaunay
|
||||
Qu Pd2 G >eg.18b.furthest-up.2-3</a></h3>
|
||||
|
||||
<p>This is the upper convex hull of the preceding example. The
|
||||
furthest-site Delaunay triangulation is the projection of the
|
||||
upper convex hull back to the input points. The furthest-site
|
||||
Voronoi vertices are the circumcenters of the furthest-site
|
||||
Delaunay triangles. </p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="18c">rbox 10 D2 d | qvoronoi
|
||||
Qu Gv >eg.18c.furthest.2</a></h3>
|
||||
|
||||
<p>This shows an incomplete furthest-site Voronoi diagram. It
|
||||
only shows regions with more than two vertices. The regions are
|
||||
artificially truncated. The actual regions are unbounded. You can
|
||||
print the regions' vertices with 'qvoronoi Qu <a
|
||||
href="qh-opto.htm#o">o</a>'. </p>
|
||||
|
||||
<p>Use Geomview's 'obscure' menu to turn off normalization, and
|
||||
Geomview's 'cameras' menu to turn off perspective. Then load this
|
||||
with the upper convex hull.</p>
|
||||
|
||||
<h3><a href="#delaunay">»</a><a name="19">rbox 10 D3 | qvoronoi QV5
|
||||
p | qconvex G >eg.19.voronoi.region.3 </a></h3>
|
||||
|
||||
<p>This shows the Voronoi region for input site 5 of a 3-d
|
||||
Voronoi diagram.</p>
|
||||
|
||||
<h2><a href="#TOC">»</a>Facet merging for imprecision</h2>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="20">rbox r s 20 Z1 G0.2 |
|
||||
qconvex G >eg.20.cone </a></h3>
|
||||
|
||||
<p>There are two things unusual about this <a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html">cone</a>.
|
||||
One is the large flat disk at one end and the other is the
|
||||
rectangles about the middle. That's how the points were
|
||||
generated, and if those points were exact, this is the correct
|
||||
hull. But <tt>rbox</tt> used floating point arithmetic to
|
||||
generate the data. So the precise convex hull should have been
|
||||
triangles instead of rectangles. By requiring convexity, Qhull
|
||||
has recovered the original design.</p>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="21a">rbox 200 s | qhull Q0
|
||||
R0.01 Gav Po >eg.21a.roundoff.errors </a></h3>
|
||||
|
||||
<p>This is the convex hull of 200 cospherical points with
|
||||
precision errors ignored ('<a href="qh-optq.htm#Q0">Q0</a>'). To
|
||||
demonstrate the effect of roundoff error, we've added a random
|
||||
perturbation ('<a href="qh-optc.htm#Rn">R0.01</a>') to every
|
||||
distance and hyperplane calculation. Qhull, like all other convex
|
||||
hull algorithms with floating point arithmetic, makes
|
||||
inconsistent decisions and generates wildly wrong results. In
|
||||
this case, one or more facets are flipped over. These facets have
|
||||
the wrong color. You can also turn on 'normals' in Geomview's
|
||||
appearances menu and turn off 'facing normals'. There should be
|
||||
some white lines pointing in the wrong direction. These
|
||||
correspond to flipped facets. </p>
|
||||
|
||||
<p>Different machines may not produce this picture. If your
|
||||
machine generated a long error message, decrease the number of
|
||||
points or the random perturbation ('<a href="qh-optc.htm#Rn">R0.01</a>').
|
||||
If it did not report flipped facets, increase the number of
|
||||
points or perturbation.</p>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="21b">rbox 200 s | qconvex Qc
|
||||
R0.01 Gpav >eg.21b.roundoff.fixed </a></h3>
|
||||
|
||||
<p>Qhull handles the random perturbations and returns an
|
||||
imprecise <a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/fixed.html">sphere</a>.
|
||||
In this case, the output is a weak approximation to the points.
|
||||
This is because a random perturbation of '<a
|
||||
href="qh-optc.htm#Rn">R0.01 </a>' is equivalent to losing all but
|
||||
1.8 digits of precision. The outer planes float above the points
|
||||
because Qhull needs to allow for the maximum roundoff error. </p>
|
||||
<p>
|
||||
If you start with a smaller random perturbation, you
|
||||
can use joggle ('<a href="qh-optq.htm#QJn">QJn</a>') to avoid
|
||||
precision problems. You need to set <i>n</i> significantly
|
||||
larger than the random perturbation. For example, try
|
||||
'rbox 200 s | qconvex Qc R1e-4 QJ1e-1'.
|
||||
|
||||
<h3><a href="#merge">»</a><a name="22a">rbox 1000 s| qconvex C0.01
|
||||
Qc Gcrp >eg.22a.merge.sphere.01</a></h3>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="22b">rbox 1000 s| qconvex
|
||||
C-0.01 Qc Gcrp >eg.22b.merge.sphere.-01</a></h3>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="22c">rbox 1000 s| qconvex C0.05
|
||||
Qc Gcrpv >eg.22c.merge.sphere.05</a></h3>
|
||||
|
||||
<h3><a href="#merge">»</a><a name="22d">rbox 1000 s| qconvex
|
||||
C-0.05 Qc Gcrpv >eg.22d.merge.sphere.-05</a></h3>
|
||||
|
||||
<p>The next four examples compare post-merging and pre-merging ('<a
|
||||
href="qh-optc.htm#Cn2">Cn</a>' vs. '<a href="qh-optc.htm#Cn">C-n</a>').
|
||||
Qhull uses '-' as a flag to indicate pre-merging.</p>
|
||||
|
||||
<p>Post-merging happens after the convex hull is built. During
|
||||
post-merging, Qhull repeatedly merges an independent set of
|
||||
non-convex facets. For a given set of parameters, the result is
|
||||
about as good as one can hope for.</p>
|
||||
|
||||
<p>Pre-merging does the same thing as post-merging, except that
|
||||
it happens after adding each point to the convex hull. With
|
||||
pre-merging, Qhull guarantees a convex hull, but the facets are
|
||||
wider than those from post-merging. If a pre-merge option is not
|
||||
specified, Qhull handles machine round-off errors.</p>
|
||||
|
||||
<p>You may see coplanar points appearing slightly outside
|
||||
the facets of the last example. This is becomes Geomview moves
|
||||
line segments forward toward the viewer. You can avoid the
|
||||
effect by setting 'lines closer' to '0' in Geomview's camera menu.
|
||||
|
||||
<h3><a href="#merge">»</a><a name="23">rbox 1000 | qconvex s
|
||||
Gcprvah C0.1 Qc >eg.23.merge.cube</a></h3>
|
||||
|
||||
<p>Here's the 3-d imprecise cube with all of the Geomview
|
||||
options. There's spheres for the vertices, radii for the coplanar
|
||||
points, dots for the interior points, hyperplane intersections,
|
||||
centrums, and inner and outer planes. The radii are shorter than
|
||||
the spheres because this uses post-merging ('<a href="qh-optc.htm#Cn2">C0.1</a>')
|
||||
instead of pre-merging.
|
||||
|
||||
<h2><a href="#TOC">»</a>4-d objects</h2>
|
||||
|
||||
<p>With Qhull and Geomview you can develop an intuitive sense of
|
||||
4-d surfaces. When you get into trouble, think of viewing the
|
||||
surface of a 3-d sphere in a 2-d plane.</p>
|
||||
|
||||
<h3><a href="#4d">»</a><a name="24">rbox 5000 D4 | qconvex s GD0v
|
||||
Pd0:0.5 C-0.02 C0.1 >eg.24.merge.cube.4d-in-3d</a></h3>
|
||||
|
||||
<p>Here's one facet of the imprecise cube in 4-d. It's projected
|
||||
into 3-d (the '<a href="qh-optg.htm#GDn">GDn</a>' option drops
|
||||
dimension n). Each ridge consists of two triangles between this
|
||||
facet and the neighboring facet. In this case, Geomview displays
|
||||
the topological ridges, i.e., as triangles between three
|
||||
vertices. That is why the cube looks lopsided.</p>
|
||||
|
||||
<h3><a href="#4d">»</a><a name="30">rbox 5000 D4 | qconvex s
|
||||
C-0.02 C0.1 Gh >eg.30.4d.merge.cube </a></h3>
|
||||
|
||||
<p><a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html">Here</a>
|
||||
is the equivalent in 4-d of the imprecise <a href="#06">square</a>
|
||||
and imprecise <a href="#23">cube</a>. It's the imprecise convex
|
||||
hull of 5000 random points in a hypercube. It's a full 4-d object
|
||||
so Geomview's <tt>ginsu </tt>does not work. If you view it in
|
||||
Geomview, you'll be inside the hypercube. To view 4-d objects
|
||||
directly, either load the <tt>4dview</tt> module or the <tt>ndview
|
||||
</tt>module. For <tt>4dview</tt>, you must have started Geomview
|
||||
in the same directory as the object. For <tt>ndview</tt>,
|
||||
initialize a set of windows with the prefab menu, and load the
|
||||
object through Geomview. The <tt>4dview</tt> module includes an
|
||||
option for slicing along any hyperplane. If you do this in the x,
|
||||
y, or z plane, you'll see the inside of a hypercube.</p>
|
||||
|
||||
<p>The '<a href="qh-optg.htm#Gh">Gh</a>' option prints the
|
||||
geometric intersections between adjacent facets. Note the strong
|
||||
convexity constraint for post-merging ('<a href="qh-optc.htm#Cn2">C0.1</a>').
|
||||
It deletes the small facets.</p>
|
||||
|
||||
<h3><a href="#4d">»</a><a name="31">rbox 20 D3 | qdelaunay G
|
||||
>eg.31.4d.delaunay </a></h3>
|
||||
|
||||
<p>The Delaunay triangulation of 3-d sites corresponds to a 4-d
|
||||
convex hull. You can't see 4-d directly but each facet is a 3-d
|
||||
object that you can project to 3-d. This is exactly the same as
|
||||
projecting a 2-d facet of a soccer ball onto a plane.</p>
|
||||
|
||||
<p>Here we see all of the facets together. You can use Geomview's
|
||||
<tt>ndview</tt> to look at the object from several directions.
|
||||
Try turning on edges in the appearance menu. You'll notice that
|
||||
some edges seem to disappear. That's because the object is
|
||||
actually two sets of overlapping facets.</p>
|
||||
|
||||
<p>You can slice the object apart using Geomview's <tt>4dview</tt>.
|
||||
With <tt>4dview</tt>, try slicing along the w axis to get a
|
||||
single set of facets and then slice along the x axis to look
|
||||
inside. Another interesting picture is to slice away the equator
|
||||
in the w dimension.</p>
|
||||
|
||||
<h3><a href="#4d">»</a><a name="32">rbox 30 s D4 | qconvex s G
|
||||
Pd0d1d2D3</a></h3>
|
||||
|
||||
<p>This is the positive octant of the convex hull of 30 4-d
|
||||
points. When looking at 4-d, it's easier to look at just a few
|
||||
facets at once. If you picked a facet that was directly above
|
||||
you, then that facet looks exactly the same in 3-d as it looks in
|
||||
4-d. If you pick several facets, then you need to imagine that
|
||||
the space of a facet is rotated relative to its neighbors. Try
|
||||
Geomview's <tt>ndview</tt> on this example.</p>
|
||||
|
||||
<h2><a href="#TOC">»</a>Halfspace intersections</h2>
|
||||
|
||||
<h3><a href="#half">»</a><a name="33a">rbox 10 r s Z1 G0.3 |
|
||||
qconvex G >eg.33a.cone </a></h3>
|
||||
|
||||
<h3><a href="#half">»</a><a name="33b">rbox 10 r s Z1 G0.3 |
|
||||
qconvex FV n | qhalf G >eg.33b.cone.dual</a></h3>
|
||||
|
||||
<h3><a href="#half">»</a><a name="33c">rbox 10 r s Z1 G0.3 |
|
||||
qconvex FV n | qhalf Fp | qconvex G >eg.33c.cone.halfspace</a></h3>
|
||||
|
||||
<p>These examples illustrate halfspace intersection. The first
|
||||
picture is the convex hull of two 20-gons plus an apex. The
|
||||
second picture is the dual of the first. Try loading <a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html">both</a>
|
||||
at once. Each vertex of the second picture corresponds to a facet
|
||||
or halfspace of the first. The vertices with four edges
|
||||
correspond to a facet with four neighbors. Similarly the facets
|
||||
correspond to vertices. A facet's normal coefficients divided by
|
||||
its negative offset is the vertice's coordinates. The coordinates
|
||||
are the intersection of the original halfspaces. </p>
|
||||
|
||||
<p>The third picture shows how Qhull can go back and forth
|
||||
between equivalent representations. It starts with a cone,
|
||||
generates the halfspaces that define each facet of the cone, and
|
||||
then intersects these halfspaces. Except for roundoff error, the
|
||||
third picture is a duplicate of the first. </p>
|
||||
<!-- Navigation links -->
|
||||
<hr>
|
||||
|
||||
<p><b>Up:</b> <a href="http://www.qhull.org">Home
|
||||
page for Qhull</a> <br>
|
||||
<b>Up:</b> <a href="index.htm#TOC">Qhull manual: Table of Contents</a><br>
|
||||
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To: </b><a href="#TOC">Qhull examples: Table of Contents</a> (please wait
|
||||
while loading)<br>
|
||||
<!-- 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>
|
||||
1547
xs/src/qhull/html/qh-faq.htm
Normal file
106
xs/src/qhull/html/qh-get.htm
Normal file
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull Downloads</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<p><b>Up:</b> <a href="http://www.qhull.org"><i>Qhull Home Page</i></a><br>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1><a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
|
||||
src="../html/qh--cone.gif" alt="[CONE]" align="middle"
|
||||
width="100" height="100"></a> Qhull Downloads</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://www.qhull.org">Qhull Home Page</a> <p>Qhull
|
||||
computes the convex hull, Delaunay triangulation, Voronoi diagram, halfspace
|
||||
intersection about a point, furthest-site Delaunay
|
||||
triangulation, and furthest-site Voronoi diagram. It
|
||||
runs in 2-d, 3-d, 4-d, and higher dimensions. It
|
||||
implements the Quickhull algorithm for computing the
|
||||
convex hull. Qhull handles roundoff errors from floating
|
||||
point arithmetic. It can approximate a convex hull. </p>
|
||||
|
||||
<p>Visit <a href="http://www.qhull.org/news">Qhull News</a>
|
||||
for news, bug reports, change history, and users.
|
||||
If you use Qhull 2003.1 or 2009.1, please upgrade to 2015.2 or apply
|
||||
<a href="http://www.qhull.org/download/poly.c-qh_gethash.patch">poly.c-qh_gethash.patch</a>.</p>
|
||||
</li>
|
||||
<li><a
|
||||
href="http://www.qhull.org/download/qhull-2015.2.zip">Download:
|
||||
Qhull 2015.2 for Windows 10, 8, 7, XP, and NT</a> (2.6 MB,
|
||||
<a href="http://www.qhull.org/README.txt">readme</a>,
|
||||
<a href="http://www.qhull.org/download/qhull-2015.2.md5sum">md5sum</a>,
|
||||
<a href="http://www.qhull.org/download/qhull-2015.2.zip.md5sum">contents</a>)
|
||||
<p>Type: console programs for Windows (32- or 64-bit)</p>
|
||||
<p>Includes 32-bit executables, documentation, and sources files. It runs in a
|
||||
command window. Qhull may be compiled for 64-bits.</p>
|
||||
</li>
|
||||
<li><a href="http://github.com/qhull/qhull/wiki">GitHub Qhull</a> (git@github.com:qhull/qhull.git)
|
||||
<p>Type: git repository for Qhull. See recent <a href="https://github.com/qhull/qhull/blob/master/src/Changes.txt">Changes.txt</a></p>
|
||||
<p>Includes documentation, source files, C++ interface, and test programs. It builds with gcc, mingw,
|
||||
CMake, DevStudio, and Qt Creator.
|
||||
</p>
|
||||
</li>
|
||||
<li><a href="http://www.qhull.org/download/qhull-2015-src-7.2.0.tgz">Download: Qhull 2015.2 for Unix</a> (1.0 MB,
|
||||
<a href="http://www.qhull.org/README.txt">readme</a>,
|
||||
<a href="http://www.qhull.org/download/qhull-2015.2.md5sum">md5sum</a>,
|
||||
<a href="http://www.qhull.org/download/qhull-2015-src-7.2.0.tgz.md5sum">contents</a>)
|
||||
<p>Type: C/C++ source code for 32-bit and 64-bit architectures. See <a href="http://www.qhull.org/src/Changes.txt">Changes.txt</a></p>
|
||||
<p>Includes documentation, source files, Makefiles, CMakeLists.txt, DevStudio projects, and Qt projects.
|
||||
Includes preliminary C++ support.</p>
|
||||
<p>Download and search sites for pre-built packages include
|
||||
<ul>
|
||||
<li><a href="https://launchpad.net/ubuntu/+source/qhull">https://launchpad.net/ubuntu/+source/qhull</a>
|
||||
<li><a href="http://software.opensuse.org/download.html?project=openSUSE%3AFactory&package=qhull">http://software.opensuse.org/download.html?project=openSUSE%3AFactory&package=qhull</a>
|
||||
<li><a href="https://www.archlinux.org/packages/extra/i686/qhull/">https://www.archlinux.org/packages/extra/i686/qhull/</a>
|
||||
<li><a href="http://rpmfind.net/linux/rpm2html/search.php?query=qhull">http://rpmfind.net/linux/rpm2html/search.php?query=qhull</a>
|
||||
<li><a href="http://rpm.pbone.net/index.php3/stat/3/srodzaj/1/search/qhull">http://rpm.pbone.net/index.php3/stat/3/srodzaj/1/search/qhull</a>
|
||||
</ul></p>
|
||||
</li>
|
||||
|
||||
<li><a
|
||||
href="http://dl.acm.org/author_page.cfm?id=81100129162">Download:
|
||||
Article about Qhull</a> (307K)
|
||||
<p>Type: PDF on ACM Digital Library (from this page only)</p>
|
||||
<p>Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T.,
|
||||
"The Quickhull algorithm for convex hulls," <i>ACM
|
||||
Transactions on Mathematical Software</i>, 22(4):469-483, Dec 1996 [<a
|
||||
href="http://portal.acm.org/citation.cfm?doid=235815.235821">abstract</a>].</p>
|
||||
</li>
|
||||
|
||||
<li><a
|
||||
href="http://www.qhull.org/download/qhull-1.0.tar.gz">Download:
|
||||
Qhull version 1.0</a> (92K) <p>Type: C source code for
|
||||
32-bit architectures </p>
|
||||
<p>Version 1.0 is a fifth the size of version 2.4. It
|
||||
computes convex hulls and Delaunay triangulations. If a
|
||||
precision error occurs, it stops with an error message.
|
||||
It reports an initialization error for inputs made with
|
||||
0/1 coordinates. </p>
|
||||
<p>Version 1.0 compiles on a PC with Borland C++ 4.02 for
|
||||
Win32 and DOS Power Pack. The options for rbox are
|
||||
"bcc32 -WX -w- -O2-e -erbox -lc rbox.c". The
|
||||
options for qhull are the same. [D. Zwick] </p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Navigation links -->
|
||||
<hr>
|
||||
|
||||
<p><b>Up:</b> <a href="http://www.qhull.org"><i>Qhull Home Page</i></a><br>
|
||||
<!-- GC common information --></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="http://www.geom.uiuc.edu/"><img src="../html/qh--geom.gif" alt="[HOME]"
|
||||
align="middle"></a> <i>The Geometry Center Home Page</i> </p>
|
||||
|
||||
<p>Comments to: <a href="mailto:qhull@qhull.org">qhull@qhull.org</a><br>
|
||||
</body>
|
||||
</html>
|
||||
826
xs/src/qhull/html/qh-impre.htm
Normal file
@@ -0,0 +1,826 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Imprecision in Qhull</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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="index.htm#TOC">Qhull manual</a>: Table of
|
||||
Contents<br>
|
||||
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To: </b><a href="#TOC">Qhull imprecision</a>: Table of Contents
|
||||
(please wait while loading)
|
||||
|
||||
<hr>
|
||||
<!-- Main text of document -->
|
||||
<h1><a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/4dcube.html"><img
|
||||
src="qh--4d.gif" alt="[4-d cube]" align="middle" width="100"
|
||||
height="100"></a> Imprecision in Qhull</h1>
|
||||
|
||||
<p>This section of the Qhull manual discusses the problems caused
|
||||
by coplanar points and why Qhull uses options '<a
|
||||
href="qh-optc.htm#C0">C-0</a>' or '<a href="qh-optq.htm#Qx">Qx</a>'
|
||||
by default. If you ignore precision issues with option '<a
|
||||
href="qh-optq.htm#Q0">Q0</a>', the output from Qhull can be
|
||||
arbitrarily bad. Qhull avoids precision problems if you merge facets (the default) or joggle
|
||||
the input ('<a
|
||||
href="qh-optq.htm#QJn">QJ</a>'). </p>
|
||||
|
||||
<p>Use option '<a href="qh-optt.htm#Tv">Tv</a>' to verify the
|
||||
output from Qhull. It verifies that adjacent facets are clearly
|
||||
convex. It verifies that all points are on or below all facets. </p>
|
||||
|
||||
<p>Qhull automatically tests for convexity if it detects
|
||||
precision errors while constructing the hull. </p>
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><a href="#TOP">»</a><a name="TOC">Qhull
|
||||
imprecision: Table of Contents </a></h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="#prec">Precision problems</a></li>
|
||||
<li><a href="#joggle">Merged facets or joggled input</a></li>
|
||||
<li><a href="#delaunay">Delaunay triangulations</a></li>
|
||||
<li><a href="#halfspace">Halfspace intersection/a></li>
|
||||
<li><a href="#imprecise">Merged facets</a></li>
|
||||
<li><a href="#how">How Qhull merges facets</a></li>
|
||||
<li><a href="#limit">Limitations of merged facets</a></li>
|
||||
<li><a href="#injoggle">Joggled input</a></li>
|
||||
<li><a href="#exact">Exact arithmetic</a></li>
|
||||
<li><a href="#approximate">Approximating a convex hull</a></li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="prec">Precision problems</a></h2>
|
||||
|
||||
<p>Since Qhull uses floating point arithmetic, roundoff error
|
||||
occurs with each calculation. This causes problems for
|
||||
geometric algorithms. Other floating point codes for convex
|
||||
hulls, Delaunay triangulations, and Voronoi diagrams also suffer
|
||||
from these problems. Qhull handles most of them.</p>
|
||||
|
||||
<p>There are several kinds of precision errors:</p>
|
||||
|
||||
<ul>
|
||||
<li>Representation error occurs when there are not enough
|
||||
digits to represent a number, e.g., 1/3.</li>
|
||||
<li>Measurement error occurs when the input coordinates are
|
||||
from measurements. </li>
|
||||
<li>Roundoff error occurs when a calculation is rounded to a
|
||||
fixed number of digits, e.g., a floating point
|
||||
calculation.</li>
|
||||
<li>Approximation error occurs when the user wants an
|
||||
approximate result because the exact result contains too
|
||||
much detail.</li>
|
||||
</ul>
|
||||
|
||||
<p>Under imprecision, calculations may return erroneous results.
|
||||
For example, roundoff error can turn a small, positive number
|
||||
into a small, negative number. See Milenkovic [<a
|
||||
href="index.htm#mile93">'93</a>] for a discussion of <em>strict
|
||||
robust geometry</em>. Qhull does not meet Milenkovic's criterion
|
||||
for accuracy. Qhull's error bound is empirical instead of
|
||||
theoretical.</p>
|
||||
|
||||
<p>Qhull 1.0 checked for precision errors but did not handle
|
||||
them. The output could contain concave facets, facets with
|
||||
inverted orientation ("flipped" facets), more than two
|
||||
facets adjacent to a ridge, and two facets with exactly the same
|
||||
set of vertices.</p>
|
||||
|
||||
<p>Qhull 2.4 and later automatically handles errors due to
|
||||
machine round-off. Option '<a href="qh-optc.htm#C0">C-0</a>' or '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>' is set by default. In 5-d and
|
||||
higher, the output is clearly convex but an input point could be
|
||||
outside of the hull. This may be corrected by using option '<a
|
||||
href="qh-optc.htm#C0">C-0</a>', but then the output may contain
|
||||
wide facets.</p>
|
||||
|
||||
<p>Qhull 2.5 and later provides option '<a href="qh-optq.htm#QJn">QJ</a>'
|
||||
to joggled input. Each input coordinate is modified by a
|
||||
small, random quantity. If a precision error occurs, a larger
|
||||
modification is tried. When no precision errors occur, Qhull is
|
||||
done. </p>
|
||||
|
||||
<p>Qhull 3.1 and later provides option '<a href="qh-optq.htm#Qt">Qt</a>'
|
||||
for triangulated output. This removes the need for
|
||||
joggled input ('<a href="qh-optq.htm#QJn">QJ</a>').
|
||||
Non-simplicial facets are triangulated.
|
||||
The facets may have zero area.
|
||||
Triangulated output is particularly useful for Delaunay triangulations.</p>
|
||||
|
||||
<p>By handling round-off errors, Qhull can provide a variety of
|
||||
output formats. For example, it can return the halfspace that
|
||||
defines each facet ('<a href="qh-opto.htm#n">n</a>'). The
|
||||
halfspaces include roundoff error. If the halfspaces were exact,
|
||||
their intersection would return the original extreme points. With
|
||||
imprecise halfspaces and exact arithmetic, nearly incident points
|
||||
may be returned for an original extreme point. By handling
|
||||
roundoff error, Qhull returns one intersection point for each of
|
||||
the original extreme points. Qhull may split or merge an extreme
|
||||
point, but this appears to be unlikely.</p>
|
||||
|
||||
<p>The following pipe implements the identity function for
|
||||
extreme points (with roundoff):
|
||||
<blockquote>
|
||||
qconvex <a href="qh-optf.htm#FV">FV</a> <a
|
||||
href="qh-opto.htm#n">n</a> | qhalf <a href="qh-optf.htm#Fp">Fp</a>
|
||||
</blockquote>
|
||||
|
||||
<p>Bernd Gartner published his
|
||||
<a href=http://www.inf.ethz.ch/personal/gaertner/miniball.html>Miniball</a>
|
||||
algorithm ["Fast and robust smallest enclosing balls", <i>Algorithms - ESA '99</i>, LNCS 1643].
|
||||
It uses floating point arithmetic and a carefully designed primitive operation.
|
||||
It is practical to 20-D or higher, and identifies at least two points on the
|
||||
convex hull of the input set. Like Qhull, it is an incremental algorithm that
|
||||
processes points furthest from the intermediate result and ignores
|
||||
points that are close to the intermediate result.
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="joggle">Merged facets or joggled input</a></h2>
|
||||
|
||||
<p>This section discusses the choice between merged facets and joggled input.
|
||||
By default, Qhull uses merged facets to handle
|
||||
precision problems. With option '<a href="qh-optq.htm#QJn">QJ</a>',
|
||||
the input is joggled. See <a href="qh-eg.htm#joggle">examples</a>
|
||||
of joggled input and triangulated output.
|
||||
<ul>
|
||||
<li>Use merged facets (the default)
|
||||
when you want non-simplicial output (e.g., the faces of a cube).
|
||||
<li>Use merged facets and triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') when
|
||||
you want simplicial output and coplanar facets (e.g., triangles for a Delaunay triangulation).
|
||||
<li>Use joggled input ('<a href="qh-optq.htm#QJn">QJ</a>') when you need clearly-convex,
|
||||
simplicial output.
|
||||
</ul>
|
||||
|
||||
<p>The choice between merged facets and joggled input depends on
|
||||
the application. Both run about the same speed. Joggled input may
|
||||
be faster if the initial joggle is sufficiently large to avoid
|
||||
precision errors.
|
||||
|
||||
<p>Most applications should used merged facets
|
||||
with triangulated output. </p>
|
||||
|
||||
<p>Use merged facets (the
|
||||
default, '<a href="qh-optc.htm#C0">C-0</a>')
|
||||
or triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') if </p>
|
||||
|
||||
<ul>
|
||||
<li>Your application supports non-simplicial facets, or
|
||||
it allows degenerate, simplicial facets (option '<a href="qh-optq.htm#Qt">Qt</a>'). </li>
|
||||
<li>You do not want the input modified. </li>
|
||||
<li>You want to set additional options for approximating the
|
||||
hull. </li>
|
||||
<li>You use single precision arithmetic (<a href="../src/libqhull/user.h#realT">realT</a>).
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Use joggled input ('<a href="qh-optq.htm#QJn">QJ</a>') if </p>
|
||||
|
||||
<ul>
|
||||
<li>Your application needs clearly convex, simplicial output</li>
|
||||
<li>Your application supports perturbed input points and narrow triangles.</li>
|
||||
<li>Seven significant digits is sufficient accuracy.</li>
|
||||
</ul>
|
||||
|
||||
<p>You may use both techniques or combine joggle with
|
||||
post-merging ('<a href="qh-optc.htm#Cn2">Cn</a>'). </p>
|
||||
|
||||
<p>Other researchers have used techniques similar to joggled
|
||||
input. Sullivan and Beichel [ref?] randomly perturb the input
|
||||
before computing the Delaunay triangulation. Corkum and Wyllie
|
||||
[news://comp.graphics, 1990] randomly rotate a polytope before
|
||||
testing point inclusion. Edelsbrunner and Mucke [Symp. Comp.
|
||||
Geo., 1988] and Yap [J. Comp. Sys. Sci., 1990] symbolically
|
||||
perturb the input to remove singularities. </p>
|
||||
|
||||
<p>Merged facets ('<a href="qh-optc.htm#C0">C-0</a>') handles
|
||||
precision problems directly. If a precision problem occurs, Qhull
|
||||
merges one of the offending facets into one of its neighbors.
|
||||
Since all precision problems in Qhull are associated with one or
|
||||
more facets, Qhull will either fix the problem or attempt to merge the
|
||||
last remaining facets. </p>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="delaunay">Delaunay
|
||||
triangulations </a></h2>
|
||||
|
||||
<p>Programs that use Delaunay triangulations traditionally assume
|
||||
a triangulated input. By default, <a href=qdelaun.htm>qdelaunay</a>
|
||||
merges regions with cocircular or cospherical input sites.
|
||||
If you want a simplicial triangulation
|
||||
use triangulated output ('<a href="qh-optq.htm#Qt">Qt</a>') or joggled
|
||||
input ('<a href="qh-optq.htm#QJn">QJ</a>').
|
||||
|
||||
<p>For Delaunay triangulations, triangulated
|
||||
output should produce good results. All points are within roundoff error of
|
||||
a paraboloid. If two points are nearly incident, one will be a
|
||||
coplanar point. So all points are clearly separated and convex.
|
||||
If qhull reports deleted vertices, the triangulation
|
||||
may contain serious precision faults. Deleted vertices are reported
|
||||
in the summary ('<a href="qh-opto.htm#s">s</a>', '<a href="qh-optf.htm#Fs">Fs</a>'</p>
|
||||
|
||||
<p>You should use option '<a href="qh-optq.htm#Qbb">Qbb</a>' with Delaunay
|
||||
triangulations. It scales the last coordinate and may reduce
|
||||
roundoff error. It is automatically set for <a href=qdelaun.htm>qdelaunay</a>,
|
||||
<a href=qvoronoi.htm>qvoronoi</a>, and option '<a
|
||||
href="qh-optq.htm#QJn">QJ</a>'.</p>
|
||||
|
||||
<p>Edelsbrunner, H, <i>Geometry and Topology for Mesh Generation</i>, Cambridge University Press, 2001.
|
||||
Good mathematical treatise on Delaunay triangulation and mesh generation for 2-d
|
||||
and 3-d surfaces. The chapter on surface simplification is
|
||||
particularly interesting. It is similar to facet merging in Qhull.
|
||||
|
||||
<p>Veron and Leon published an algorithm for shape preserving polyhedral
|
||||
simplification with bounded error [<i>Computers and Graphics</i>, 22.5:565-585, 1998].
|
||||
It remove nodes using front propagation and multiple remeshing.
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="halfspace">Halfspace intersection</a></h2>
|
||||
|
||||
<p>
|
||||
The identity pipe for Qhull reveals some precision questions for
|
||||
halfspace intersections. The identity pipe creates the convex hull of
|
||||
a set of points and intersects the facets' hyperplanes. It should return the input
|
||||
points, but narrow distributions may drop points while offset distributions may add
|
||||
points. It may be better to normalize the input set about the origin.
|
||||
For example, compare the first results with the later two results: [T. Abraham]
|
||||
<blockquote>
|
||||
rbox 100 s t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
|
||||
<br>
|
||||
rbox 100 L1e5 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
|
||||
<br>
|
||||
rbox 100 s O10 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail
|
||||
</blockquote>
|
||||
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="imprecise">Merged facets </a></h2>
|
||||
|
||||
<p>Qhull detects precision
|
||||
problems when computing distances. A precision problem occurs if
|
||||
the distance computation is less than the maximum roundoff error.
|
||||
Qhull treats the result of a hyperplane computation as if it
|
||||
were exact.</p>
|
||||
|
||||
<p>Qhull handles precision problems by merging non-convex facets.
|
||||
The result of merging two facets is a thick facet defined by an <i>inner
|
||||
plane</i> and an <i>outer plane</i>. The inner and outer planes
|
||||
are offsets from the facet's hyperplane. The inner plane is
|
||||
clearly below the facet's vertices. At the end of Qhull, the
|
||||
outer planes are clearly above all input points. Any exact convex
|
||||
hull must lie between the inner and outer planes.</p>
|
||||
|
||||
<p>Qhull tests for convexity by computing a point for each facet.
|
||||
This point is called the facet's <i>centrum</i>. It is the
|
||||
arithmetic center of the facet's vertices projected to the
|
||||
facet's hyperplane. For simplicial facets with <em>d</em>
|
||||
vertices, the centrum is equivalent to the centroid or center of
|
||||
gravity. </p>
|
||||
|
||||
<p>Two neighboring facets are convex if each centrum is clearly
|
||||
below the other hyperplane. The '<a href="qh-optc.htm#Cn2">Cn</a>'
|
||||
or '<a href="qh-optc.htm#Cn">C-n</a>' options sets the centrum's
|
||||
radius to <i>n </i>. A centrum is clearly below a hyperplane if
|
||||
the computed distance from the centrum to the hyperplane is
|
||||
greater than the centrum's radius plus two maximum roundoff
|
||||
errors. Two are required because the centrum can be the maximum
|
||||
roundoff error above its hyperplane and the distance computation
|
||||
can be high by the maximum roundoff error.</p>
|
||||
|
||||
<p>With the '<a href="qh-optc.htm#Cn">C-n</a>' or '<a
|
||||
href="qh-optc.htm#An">A-n </a>' options, Qhull merges non-convex
|
||||
facets while constructing the hull. The remaining facets are
|
||||
clearly convex. With the '<a href="qh-optq.htm#Qx">Qx </a>'
|
||||
option, Qhull merges coplanar facets after constructing the hull.
|
||||
While constructing the hull, it merges coplanar horizon facets,
|
||||
flipped facets, concave facets and duplicated ridges. With '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>', coplanar points may be missed, but
|
||||
it appears to be unlikely.</p>
|
||||
|
||||
<p>If the user sets the '<a href="qh-optc.htm#An2">An</a>' or '<a
|
||||
href="qh-optc.htm#An">A-n</a>' option, then all neighboring
|
||||
facets are clearly convex and the maximum (exact) cosine of an
|
||||
angle is <i>n</i>.</p>
|
||||
|
||||
<p>If '<a href="qh-optc.htm#C0">C-0</a>' or '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>' is used without other precision
|
||||
options (default), Qhull tests vertices instead of centrums for
|
||||
adjacent simplices. In 3-d, if simplex <i>abc</i> is adjacent to
|
||||
simplex <i>bcd</i>, Qhull tests that vertex <i>a</i> is clearly
|
||||
below simplex <i>bcd </i>, and vertex <i>d</i> is clearly below
|
||||
simplex <i>abc</i>. When building the hull, Qhull tests vertices
|
||||
if the horizon is simplicial and no merges occur. </p>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="how">How Qhull merges facets</a></h2>
|
||||
|
||||
<p>If two facets are not clearly convex, then Qhull removes one
|
||||
or the other facet by merging the facet into a neighbor. It
|
||||
selects the merge which minimizes the distance from the
|
||||
neighboring hyperplane to the facet's vertices. Qhull also
|
||||
performs merges when a facet has fewer than <i>d</i> neighbors (called a
|
||||
degenerate facet), when a facet's vertices are included in a
|
||||
neighboring facet's vertices (called a redundant facet), when a
|
||||
facet's orientation is flipped, or when a ridge occurs between
|
||||
more than two facets.</p>
|
||||
|
||||
<p>Qhull performs merges in a series of passes sorted by merge
|
||||
angle. Each pass merges those facets which haven't already been
|
||||
merged in that pass. After a pass, Qhull checks for redundant
|
||||
vertices. For example, if a vertex has only two neighbors in 3-d,
|
||||
the vertex is redundant and Qhull merges it into an adjacent
|
||||
vertex.</p>
|
||||
|
||||
<p>Merging two simplicial facets creates a non-simplicial facet
|
||||
of <em>d+1</em> vertices. Additional merges create larger facets.
|
||||
When merging facet A into facet B, Qhull retains facet B's
|
||||
hyperplane. It merges the vertices, neighbors, and ridges of both
|
||||
facets. It recomputes the centrum if a wide merge has not
|
||||
occurred (qh_WIDEcoplanar) and the number of extra vertices is
|
||||
smaller than a constant (qh_MAXnewcentrum).</p>
|
||||
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="limit">Limitations of merged
|
||||
facets</a></h2>
|
||||
|
||||
<ul>
|
||||
<li><b>Uneven dimensions</b> --
|
||||
If one coordinate has a larger absolute value than other
|
||||
coordinates, it may dominate the effect of roundoff errors on
|
||||
distance computations. You may use option '<a
|
||||
href="qh-optq.htm#QbB">QbB</a>' to scale points to the unit cube.
|
||||
For Delaunay triangulations and Voronoi diagrams, <a href=qdelaun.htm>qdelaunay</a>
|
||||
and <a href=qvoronoi.htm>qvoronoi</a> always set
|
||||
option '<a href="qh-optq.htm#Qbb">Qbb</a>'. It scales the last
|
||||
coordinate to [0,m] where <i>m</i> is the maximum width of the
|
||||
other coordinates. Option '<a href="qh-optq.htm#Qbb">Qbb</a>' is
|
||||
needed for Delaunay triangulations of integer coordinates
|
||||
and nearly cocircular points.
|
||||
|
||||
<p>For example, compare
|
||||
<pre>
|
||||
rbox 1000 W0 t | qconvex Qb2:-1e-14B2:1e-14
|
||||
</pre>
|
||||
with
|
||||
<pre>
|
||||
rbox 1000 W0 t | qconvex
|
||||
</pre>
|
||||
The distributions are the same but the first is compressed to a 2e-14 slab.
|
||||
<p>
|
||||
<li><b>Post-merging of coplanar facets</b> -- In 5-d and higher, option '<a href="qh-optq.htm#Qx">Qx</a>'
|
||||
(default) delays merging of coplanar facets until post-merging.
|
||||
This may allow "dents" to occur in the intermediate
|
||||
convex hulls. A point may be poorly partitioned and force a poor
|
||||
approximation. See option '<a href="qh-optq.htm#Qx">Qx</a>' for
|
||||
further discussion.</p>
|
||||
|
||||
<p>This is difficult to produce in 5-d and higher. Option '<a href="qh-optq.htm#Q6">Q6</a>' turns off merging of concave
|
||||
facets. This is similar to 'Qx'. It may lead to serious precision errors,
|
||||
for example,
|
||||
<pre>
|
||||
rbox 10000 W1e-13 | qhull Q6 Tv
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<li><b>Maximum facet width</b> --
|
||||
Qhull reports the maximum outer plane and inner planes (if
|
||||
more than roundoff error apart). There is no upper bound
|
||||
for either figure. This is an area for further research. Qhull
|
||||
does a good job of post-merging in all dimensions. Qhull does a
|
||||
good job of pre-merging in 2-d, 3-d, and 4-d. With the '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>' option, it does a good job in
|
||||
higher dimensions. In 5-d and higher, Qhull does poorly at
|
||||
detecting redundant vertices. </p>
|
||||
|
||||
<p>In the summary ('<a href="qh-opto.htm#s">s</a>'), look at the
|
||||
ratio between the maximum facet width and the maximum width of a
|
||||
single merge, e.g., "(3.4x)". Qhull usually reports a
|
||||
ratio of four or lower in 3-d and six or lower in 4-d. If it
|
||||
reports a ratio greater than 10, this may indicate an
|
||||
implementation error. Narrow distributions (see following) may
|
||||
produce wide facets.
|
||||
|
||||
<p>For example, if special processing for narrow distributions is
|
||||
turned off ('<a href="qh-optq.htm#Q10">Q10</a>'), qhull may produce
|
||||
a wide facet:</p>
|
||||
<pre>
|
||||
rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<li><b>Narrow distribution</b> -- In 3-d, a narrow distribution may result in a poor
|
||||
approximation. For example, if you do not use qdelaunay nor option
|
||||
'<a href="qh-optq.htm#Qbb">Qbb</a>', the furthest-site
|
||||
Delaunay triangulation of nearly cocircular points may produce a poor
|
||||
approximation:
|
||||
<pre>
|
||||
rbox s 5000 W1e-13 D2 t1002151341 | qhull d Qt
|
||||
rbox 1000 s W1e-13 t1002231672 | qhull d Tv
|
||||
</pre>
|
||||
|
||||
<p>During
|
||||
construction of the hull, a point may be above two
|
||||
facets with opposite orientations that span the input
|
||||
set. Even though the point may be nearly coplanar with both
|
||||
facets, and can be distant from the precise convex
|
||||
hull of the input sites. Additional facets leave the point distant from
|
||||
a facet. To fix this problem, add option '<a href="qh-optq.htm#Qbb">Qbb</a>'
|
||||
(it scales the last coordinate). Option '<a href="qh-optq.htm#Qbb">Qbb</a>'
|
||||
is automatically set for <a href=qdelaun.htm>qdelaunay</a> and <a href=qvoronoi.htm>qvoronoi</a>.
|
||||
|
||||
<p>Qhull generates a warning if the initial simplex is narrow.
|
||||
For narrow distributions, Qhull changes how it processes coplanar
|
||||
points -- it does not make a point coplanar until the hull is
|
||||
finished.
|
||||
Use option '<a href="qh-optq.htm#Q10">Q10</a>' to try Qhull without
|
||||
special processing for narrow distributions.
|
||||
For example, special processing is needed for:
|
||||
<pre>
|
||||
rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
|
||||
</pre>
|
||||
|
||||
<p>You may turn off the warning message by reducing
|
||||
qh_WARNnarrow in <tt>user.h</tt> or by setting option
|
||||
'<a href="qh-optp.htm#Pp">Pp</a>'. </p>
|
||||
|
||||
<p>Similar problems occur for distributions with a large flat facet surrounded
|
||||
with many small facet at a sharp angle to the large facet.
|
||||
Qhull 3.1 fixes most of these problems, but a poor approximation can occur.
|
||||
A point may be left outside of the convex hull ('<a href="qh-optt.htm#Tv">Tv</a>').
|
||||
Examples include
|
||||
the furthest-site Delaunay triangulation of nearly cocircular points plus the origin, and the convex hull of a cone of nearly cocircular points. The width of the band is 10^-13.
|
||||
<pre>
|
||||
rbox s 1000 W1e-13 P0 D2 t996799242 | qhull d Tv
|
||||
rbox 1000 s Z1 G1e-13 t1002152123 | qhull Tv
|
||||
rbox 1000 s Z1 G1e-13 t1002231668 | qhull Tv
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<li><b>Quadratic running time</b> -- If the output contains large, non-simplicial
|
||||
facets, the running time for Qhull may be quadratic in the size of the triangulated
|
||||
output. For example, <tt>rbox 1000 s W1e-13 c G2 | qhull d</tt> is 4 times
|
||||
faster for 500 points. The convex hull contains two large nearly spherical facets and
|
||||
many nearly coplanar facets. Each new point retriangulates the spherical facet and repartitions the remaining points into all of the nearly coplanar facets.
|
||||
In this case, quadratic running time is avoided if you use qdelaunay,
|
||||
add option '<a href="qh-optq.htm#Qbb">Qbb</a>',
|
||||
or add the origin ('P0') to the input.
|
||||
<p>
|
||||
<li><b>Nearly coincident points within 1e-13</b> --
|
||||
Multiple, nearly coincident points within a 1e-13 ball of points in the unit cube
|
||||
may lead to wide facets or quadratic running time.
|
||||
For example, the convex hull a 1000 coincident, cospherical points in 4-D,
|
||||
or the 3-D Delaunay triangulation of nearly coincident points, may lead to very
|
||||
wide facets (e.g., 2267021951.3x).
|
||||
|
||||
<p>For Delaunay triangulations, the problem typically occurs for extreme points of the input
|
||||
set (i.e., on the edge between the upper and lower convex hull). After multiple facet merges, four
|
||||
facets may share the same, duplicate ridge and must be merged.
|
||||
Some of these facets may be long and narrow, leading to a very wide merged facet.
|
||||
If so, error QH6271 is reported. It may be overriden with option '<a href="qh-optq.htm#Q12">Q12</a>'.
|
||||
|
||||
<p>Duplicate ridges occur when the horizon facets for a new point is "pinched".
|
||||
In a duplicate ridge, a subridge (e.g., a line segment in 3-d) is shared by two horizon facets.
|
||||
At least two of its vertices are nearly coincident. It is easy to generate coincident points with
|
||||
option 'Cn,r,m' of rbox. It generates n points within an r ball for each of m input sites. For example,
|
||||
every point of the following distributions has a nearly coincident point within a 1e-13 ball.
|
||||
Substantially smaller or larger balls do not lead to pinched horizons.
|
||||
<pre>
|
||||
rbox 1000 C1,1e-13 D4 s t | qhull
|
||||
rbox 75 C1,1e-13 t | qhull d
|
||||
</pre>
|
||||
For Delaunay triangulations, a bounding box may alleviate this error (e.g., <tt>rbox 500 C1,1E-13 t c G1 | qhull d</tt>).
|
||||
A later release of qhull will avoid pinched horizons by merging duplicate subridges. A subridge is
|
||||
merged by merging adjacent vertices.
|
||||
<p>
|
||||
<li><b>Facet with zero-area</b> --
|
||||
It is possible for a zero-area facet to be convex with its
|
||||
neighbors. This can occur if the hyperplanes of neighboring
|
||||
facets are above the facet's centrum, and the facet's hyperplane
|
||||
is above the neighboring centrums. Qhull computes the facet's
|
||||
hyperplane so that it passes through the facet's vertices. The
|
||||
vertices can be collinear. </p>
|
||||
|
||||
<p>
|
||||
<li><b>No more facets</b> -- Qhull reports an error if there are <em>d+1</em> facets left
|
||||
and two of the facets are not clearly convex. This typically
|
||||
occurs when the convexity constraints are too strong or the input
|
||||
points are degenerate. The former is more likely in 5-d and
|
||||
higher -- especially with option '<a href="qh-optc.htm#Cn">C-n</a>'.</p>
|
||||
|
||||
<p>
|
||||
<li><b>Deleted cone</b> -- Lots of merging can end up deleting all
|
||||
of the new facets for a point. This is a rare event that has
|
||||
only been seen while debugging the code.
|
||||
|
||||
<p>
|
||||
<li><b>Triangulated output leads to precision problems</b> -- With sufficient
|
||||
merging, the ridges of a non-simplicial facet may have serious topological
|
||||
and geometric problems. A ridge may be between more than two
|
||||
neighboring facets. If so, their triangulation ('<a href="qh-optq.htm#Qt">Qt</a>')
|
||||
will fail since two facets have the same vertex set. Furthermore,
|
||||
a triangulated facet may have flipped orientation compared to its
|
||||
neighbors.</li>
|
||||
|
||||
<p>The triangulation process detects degenerate facets with
|
||||
only two neighbors. These are marked degenerate. They have
|
||||
zero area.
|
||||
|
||||
<p>
|
||||
<li><b>Coplanar points</b> --
|
||||
Option '<a href="qh-optq.htm#Qc">Qc</a>' is determined by
|
||||
qh_check_maxout() after constructing the hull. Qhull needs to
|
||||
retain all possible coplanar points in the facets' coplanar sets.
|
||||
This depends on qh_RATIOnearInside in <tt>user.h.</tt>
|
||||
Furthermore, the cutoff for a coplanar point is arbitrarily set
|
||||
at the minimum vertex. If coplanar points are important to your
|
||||
application, remove the interior points by hand (set '<a
|
||||
href="qh-optq.htm#Qc">Qc</a> <a href="qh-optq.htm#Qi">Qi</a>') or
|
||||
make qh_RATIOnearInside sufficiently large.</p>
|
||||
|
||||
<p>
|
||||
<li><b>Maximum roundoff error</b> -- Qhull computes the maximum roundoff error from the maximum
|
||||
coordinates of the point set. Usually the maximum roundoff error
|
||||
is a reasonable choice for all distance computations. The maximum
|
||||
roundoff error could be computed separately for each point or for
|
||||
each distance computation. This is expensive and it conflicts
|
||||
with option '<a href="qh-optc.htm#Cn">C-n</a>'.
|
||||
|
||||
<p>
|
||||
<li><b>All flipped or upper Delaunay</b> -- When a lot of merging occurs for
|
||||
Delaunay triangulations, a new point may lead to no good facets. For example,
|
||||
try a strong convexity constraint:
|
||||
<pre>
|
||||
rbox 1000 s t993602376 | qdelaunay C-1e-3
|
||||
</pre>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="injoggle">Joggled input</a></h2>
|
||||
|
||||
<p>Joggled input is a simple work-around for precision problems
|
||||
in computational geometry ["joggle: to shake or jar
|
||||
slightly," Amer. Heritage Dictionary]. Other names are
|
||||
<i>jostled input</i> or <i>random perturbation</i>.
|
||||
Qhull joggles the
|
||||
input by modifying each coordinate by a small random quantity. If
|
||||
a precision problem occurs, Qhull joggles the input with a larger
|
||||
quantity and the algorithm is restarted. This process continues
|
||||
until no precision problems occur. Unless all inputs incur
|
||||
precision problems, Qhull will terminate. Qhull adjusts the inner
|
||||
and outer planes to account for the joggled input. </p>
|
||||
|
||||
<p>Neither joggle nor merged facets has an upper bound for the width of the output
|
||||
facets, but both methods work well in practice. Joggled input is
|
||||
easier to justify. Precision errors occur when the points are
|
||||
nearly singular. For example, four points may be coplanar or
|
||||
three points may be collinear. Consider a line and an incident
|
||||
point. A precision error occurs if the point is within some
|
||||
epsilon of the line. Now joggle the point away from the line by a
|
||||
small, uniformly distributed, random quantity. If the point is
|
||||
changed by more than epsilon, the precision error is avoided. The
|
||||
probability of this event depends on the maximum joggle. Once the
|
||||
maximum joggle is larger than epsilon, doubling the maximum
|
||||
joggle will halve the probability of a precision error. </p>
|
||||
|
||||
<p>With actual data, an analysis would need to account for each
|
||||
point changing independently and other computations. It is easier
|
||||
to determine the probabilities empirically ('<a href="qh-optt.htm#TRn">TRn</a>') . For example, consider
|
||||
computing the convex hull of the unit cube centered on the
|
||||
origin. The arithmetic has 16 significant decimal digits. </p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Convex hull of unit cube</b> </p>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th align="left">joggle</th>
|
||||
<th align="right">error prob. </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.0e-15</td>
|
||||
<td align="center">0.983 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">2.0e-15</td>
|
||||
<td align="center">0.830 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">4.0e-15</td>
|
||||
<td align="center">0.561 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">8.0e-15</td>
|
||||
<td align="center">0.325 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.6e-14</td>
|
||||
<td align="center">0.185 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">3.2e-14</td>
|
||||
<td align="center">0.099 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">6.4e-14</td>
|
||||
<td align="center">0.051 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.3e-13</td>
|
||||
<td align="center">0.025 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">2.6e-13</td>
|
||||
<td align="center">0.010 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">5.1e-13</td>
|
||||
<td align="center">0.004 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.0e-12</td>
|
||||
<td align="center">0.002 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">2.0e-12</td>
|
||||
<td align="center">0.001 </td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
|
||||
<p>A larger joggle is needed for multiple points. Since the
|
||||
number of potential singularities increases, the probability of
|
||||
one or more precision errors increases. Here is an example. </p>
|
||||
|
||||
<blockquote>
|
||||
<p><b>Convex hull of 1000 points on unit cube</b> </p>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th align="left">joggle</th>
|
||||
<th align="right">error prob. </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.0e-12</td>
|
||||
<td align="center">0.870 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">2.0e-12</td>
|
||||
<td align="center">0.700 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">4.0e-12</td>
|
||||
<td align="center">0.450 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">8.0e-12</td>
|
||||
<td align="center">0.250 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.6e-11</td>
|
||||
<td align="center">0.110 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">3.2e-11</td>
|
||||
<td align="center">0.065 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">6.4e-11</td>
|
||||
<td align="center">0.030 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">1.3e-10</td>
|
||||
<td align="center">0.010 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">2.6e-10</td>
|
||||
<td align="center">0.008 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">5.1e-09</td>
|
||||
<td align="center">0.003 </td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
|
||||
<p>Other distributions behave similarly. No distribution should
|
||||
behave significantly worse. In Euclidean space, the probability
|
||||
measure of all singularities is zero. With floating point
|
||||
numbers, the probability of a singularity is non-zero. With
|
||||
sufficient digits, the probability of a singularity is extremely
|
||||
small for random data. For a sufficiently large joggle, all data
|
||||
is nearly random data. </p>
|
||||
|
||||
<p>Qhull uses an initial joggle of 30,000 times the maximum
|
||||
roundoff error for a distance computation. This avoids most
|
||||
potential singularities. If a failure occurs, Qhull retries at
|
||||
the initial joggle (in case bad luck occurred). If it occurs
|
||||
again, Qhull increases the joggle by ten-fold and tries again.
|
||||
This process repeats until the joggle is a hundredth of the width
|
||||
of the input points. Qhull reports an error after 100 attempts.
|
||||
This should never happen with double-precision arithmetic. Once
|
||||
the probability of success is non-zero, the probability of
|
||||
success increases about ten-fold at each iteration. The
|
||||
probability of repeated failures becomes extremely small. </p>
|
||||
|
||||
<p>Merged facets produces a significantly better approximation.
|
||||
Empirically, the maximum separation between inner and outer
|
||||
facets is about 30 times the maximum roundoff error for a
|
||||
distance computation. This is about 2,000 times better than
|
||||
joggled input. Most applications though will not notice the
|
||||
difference. </p>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="exact">Exact arithmetic</a></h2>
|
||||
|
||||
<p>Exact arithmetic may be used instead of floating point.
|
||||
Singularities such as coplanar points can either be handled
|
||||
directly or the input can be symbolically perturbed. Using exact
|
||||
arithmetic is slower than using floating point arithmetic and the
|
||||
output may take more space. Chaining a sequence of operations
|
||||
increases the time and space required. Some operations are
|
||||
difficult to do.</p>
|
||||
|
||||
<p>Clarkson's <a
|
||||
href="http://www.netlib.org/voronoi/hull.html">hull
|
||||
program</a> and Shewchuk's <a
|
||||
href="http://www.cs.cmu.edu/~quake/triangle.html">triangle
|
||||
program</a> are practical implementations of exact arithmetic.</p>
|
||||
|
||||
<p>Clarkson limits the input precision to about fifteen digits.
|
||||
This reduces the number of nearly singular computations. When a
|
||||
determinant is nearly singular, he uses exact arithmetic to
|
||||
compute a precise result.</p>
|
||||
|
||||
<h2><a href="#TOC">»</a><a name="approximate">Approximating a
|
||||
convex hull</a></h2>
|
||||
|
||||
<p>Qhull may be used for approximating a convex hull. This is
|
||||
particularly valuable in 5-d and higher where hulls can be
|
||||
immense. You can use '<a href="qh-optq.htm#Qx">Qx</a> <a
|
||||
href="qh-optc.htm#Cn">C-n</a>' to merge facets as the hull is
|
||||
being constructed. Then use '<a href="qh-optc.htm#Cn2">Cn</a>'
|
||||
and/or '<a href="qh-optc.htm#An2">An</a>' to merge small facets
|
||||
during post-processing. You can print the <i>n</i> largest facets
|
||||
with option '<a href="qh-optp.htm#PAn">PAn</a>'. You can print
|
||||
facets whose area is at least <i>n</i> with option '<a
|
||||
href="qh-optp.htm#PFn">PFn</a>'. You can output the outer planes
|
||||
and an interior point with '<a href="qh-optf.htm#FV">FV</a> <a
|
||||
href="qh-optf.htm#Fo">Fo</a>' and then compute their intersection
|
||||
with 'qhalf'. </p>
|
||||
|
||||
<p>To approximate a convex hull in 6-d and higher, use
|
||||
post-merging with '<a href="qh-optc.htm#Wn">Wn</a>' (e.g., qhull
|
||||
W1e-1 C1e-2 TF2000). Pre-merging with a convexity constraint
|
||||
(e.g., qhull Qx C-1e-2) often produces a poor approximation or
|
||||
terminates with a simplex. Option '<a href="qh-optq.htm#QbB">QbB</a>'
|
||||
may help to spread out the data.</p>
|
||||
|
||||
<p>You will need to experiment to determine a satisfactory set of
|
||||
options. Use <a href="rbox.htm">rbox</a> to generate test sets
|
||||
quickly and <a href="index.htm#geomview">Geomview</a> to view
|
||||
the results. You will probably want to write your own driver for
|
||||
Qhull using the Qhull library. For example, you could select the
|
||||
largest facet in each quadrant.</p>
|
||||
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a><br>
|
||||
<b>To:</b> <a href="#TOC">Qhull imprecision: Table of Contents</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>
|
||||
292
xs/src/qhull/html/qh-optc.htm
Normal file
@@ -0,0 +1,292 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull precision options</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<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> Qhull precision options</h1>
|
||||
|
||||
This section lists the precision options for Qhull. These options are
|
||||
indicated by an upper-case letter followed by a number.
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="prec">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Precision options</h2>
|
||||
|
||||
<p>Most users will not need to set these options. They are best
|
||||
used for <a href="qh-impre.htm#approximate">approximating</a> a
|
||||
convex hull. They may also be used for testing Qhull's handling
|
||||
of precision errors.</p>
|
||||
|
||||
<p>By default, Qhull uses options '<a href="#C0">C-0</a>' for
|
||||
2-d, 3-d and 4-d, and '<a href="qh-optq.htm#Qx">Qx</a>' for 5-d
|
||||
and higher. These options use facet merging to handle precision
|
||||
errors. You may also use joggled input '<a href="qh-optq.htm#QJn">QJ</a>'
|
||||
to avoid precision problems.
|
||||
For more information see <a
|
||||
href="qh-impre.htm">Imprecision in Qhull</a>.</p>
|
||||
|
||||
<dl compact>
|
||||
<dt> </dt>
|
||||
<dd><b>General</b></dd>
|
||||
<dt><a href="#Cn2">Cn</a></dt>
|
||||
<dd>centrum radius for post-merging</dd>
|
||||
<dt><a href="#Cn">C-n</a></dt>
|
||||
<dd>centrum radius for pre-merging</dd>
|
||||
<dt><a href="#An2">An</a></dt>
|
||||
<dd>cosine of maximum angle for post-merging</dd>
|
||||
<dt><a href="#An">A-n</a></dt>
|
||||
<dd>cosine of maximum angle for pre-merging</dd>
|
||||
<dt><a href="qh-optq.htm#Qx">Qx</a></dt>
|
||||
<dd>exact pre-merges (allows coplanar facets)</dd>
|
||||
<dt><a href="#C0">C-0</a></dt>
|
||||
<dd>handle all precision errors</dd>
|
||||
<dt><a href="#Wn">Wn</a></dt>
|
||||
<dd>min distance above plane for outside points</dd>
|
||||
</dl>
|
||||
|
||||
<dl compact>
|
||||
<dt> </dt>
|
||||
<dd><b>Experimental</b></dd>
|
||||
<dt><a href="#Un">Un</a></dt>
|
||||
<dd>max distance below plane for a new, coplanar point</dd>
|
||||
<dt><a href="#En">En</a></dt>
|
||||
<dd>max roundoff error for distance computation</dd>
|
||||
<dt><a href="#Vn">Vn</a></dt>
|
||||
<dd>min distance above plane for a visible facet</dd>
|
||||
<dt><a href="#Rn">Rn</a></dt>
|
||||
<dd>randomly perturb computations by a factor of [1-n,1+n]</dd>
|
||||
</dl>
|
||||
|
||||
<dl compact>
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="An">A-n - cosine of maximum
|
||||
angle for pre-merging.</a></h3>
|
||||
|
||||
<p>Pre-merging occurs while Qhull constructs the hull. It is
|
||||
indicated by '<a href="#Cn">C-n</a>', 'A-n', or '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>'.</p>
|
||||
|
||||
<p>If the angle between a pair of facet normals is greater than <i>n</i>,
|
||||
Qhull merges one of the facets into a neighbor. It selects the
|
||||
facet that is closest to a neighboring facet.</p>
|
||||
|
||||
<p>For example, option 'A-0.99' merges facets during the
|
||||
construction of the hull. If the cosine of the angle between
|
||||
facets is greater than 0.99, one or the other facet is merged.
|
||||
Qhull accounts for the maximum roundoff error.</p>
|
||||
|
||||
<p>If 'A-n' is set without '<a href="#Cn">C-n</a>', then '<a
|
||||
href="#C0">C-0</a>' is automatically set. </p>
|
||||
|
||||
<p>In 5-d and higher, you should set '<a href="qh-optq.htm#Qx">Qx</a>'
|
||||
along with 'A-n'. It skips merges of coplanar facets until after
|
||||
the hull is constructed and before '<a href="#An2">An</a>' and '<a
|
||||
href="#Cn2">Cn</a>' are checked. </p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="An2">An - cosine of maximum angle for
|
||||
post-merging.</a></h3>
|
||||
|
||||
<p>Post merging occurs after the hull is constructed. For
|
||||
example, option 'A0.99' merges a facet if the cosine of the angle
|
||||
between facets is greater than 0.99. Qhull accounts for the
|
||||
maximum roundoff error.</p>
|
||||
|
||||
<p>If 'An' is set without '<a href="#Cn2">Cn</a>', then '<a
|
||||
href="#Cn2">C0</a>' is automatically set. </p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="C0">C-0 - handle all precision
|
||||
errors </a></h3>
|
||||
|
||||
<p>Qhull handles precision errors by merging facets. The 'C-0'
|
||||
option handles all precision errors in 2-d, 3-d, and 4-d. It is
|
||||
set by default. It may be used in higher dimensions, but
|
||||
sometimes the facet width grows rapidly. It is usually better to
|
||||
use '<a href="qh-optq.htm#Qx">Qx</a>' in 5-d and higher.
|
||||
Use '<a href="qh-optq.htm#QJn">QJ</a>' to joggle the input
|
||||
instead of merging facets.
|
||||
Use '<a
|
||||
href="qh-optq.htm#Q0">Q0</a>' to turn both options off.</p>
|
||||
|
||||
<p>Qhull optimizes 'C-0' ("_zero-centrum") by testing
|
||||
vertices instead of centrums for adjacent simplices. This may be
|
||||
slower in higher dimensions if merges decrease the number of
|
||||
processed points. The optimization may be turned off by setting a
|
||||
small value such as 'C-1e-30'. See <a href="qh-impre.htm">How
|
||||
Qhull handles imprecision</a>.</p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Cn">C-n - centrum radius for
|
||||
pre-merging</a></h3>
|
||||
|
||||
<p>Pre-merging occurs while Qhull constructs the hull. It is
|
||||
indicated by 'C-n', '<a href="#An">A-n</a>', or '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>'.</p>
|
||||
|
||||
<p>The <i>centrum</i> of a facet is a point on the facet for
|
||||
testing facet convexity. It is the average of the vertices
|
||||
projected to the facet's hyperplane. Two adjacent facets are
|
||||
convex if each centrum is clearly below the other facet. </p>
|
||||
|
||||
<p>If adjacent facets are non-convex, one of the facets is merged
|
||||
into a neighboring facet. Qhull merges the facet that is closest
|
||||
to a neighboring facet. </p>
|
||||
|
||||
<p>For option 'C-n', <i>n</i> is the centrum radius. For example,
|
||||
'C-0.001' merges facets whenever the centrum is less than 0.001
|
||||
from a neighboring hyperplane. Qhull accounts for roundoff error
|
||||
when testing the centrum.</p>
|
||||
|
||||
<p>In 5-d and higher, you should set '<a href="qh-optq.htm#Qx">Qx</a>'
|
||||
along with 'C-n'. It skips merges of coplanar facets until after
|
||||
the hull is constructed and before '<a href="#An2">An</a>' and '<a
|
||||
href="#Cn2">Cn</a>' are checked. </p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Cn2">Cn - centrum radius for
|
||||
post-merging</a></h3>
|
||||
|
||||
<p>Post-merging occurs after Qhull constructs the hull. It is
|
||||
indicated by '<a href="#Cn2">Cn</a>' or '<a href="#An2">An</a>'. </p>
|
||||
|
||||
<p>For option '<a href="#Cn2">Cn</a>', <i>n</i> is the centrum
|
||||
radius. For example, 'C0.001' merges facets when the centrum is
|
||||
less than 0.001 from a neighboring hyperplane. Qhull accounts for
|
||||
roundoff error when testing the centrum.</p>
|
||||
|
||||
<p>Both pre-merging and post-merging may be defined. If only
|
||||
post-merging is used ('<a href="qh-optq.htm#Q0">Q0</a>' with
|
||||
'Cn'), Qhull may fail to produce a hull due to precision errors
|
||||
during the hull's construction.</p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="En">En - max roundoff error
|
||||
for distance computations</a></h3>
|
||||
|
||||
<p>This allows the user to change the maximum roundoff error
|
||||
computed by Qhull. The value computed by Qhull may be overly
|
||||
pessimistic. If 'En' is set too small, then the output may not be
|
||||
convex. The statistic "max. distance of a new vertex to a
|
||||
facet" (from option '<a href="qh-optt.htm#Ts">Ts</a>') is a
|
||||
reasonable upper bound for the actual roundoff error. </p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Rn">Rn - randomly perturb
|
||||
computations </a></h3>
|
||||
|
||||
<p>This option perturbs every distance, hyperplane, and angle
|
||||
computation by up to <i>(+/- n * max_coord)</i>. It simulates the
|
||||
effect of roundoff errors. Unless '<a href="#En">En</a>' is
|
||||
explicitly set, it is adjusted for 'Rn'. The command 'qhull Rn'
|
||||
will generate a convex hull despite the perturbations. See the <a
|
||||
href="qh-eg.htm#merge">Examples </a>section for an example.</p>
|
||||
|
||||
<p>Options 'Rn C-n' have the effect of '<a href="#Wn">W2n</a>'
|
||||
and '<a href="#Cn">C-2n</a>'. To use time as the random number
|
||||
seed, use option '<a href="qh-optq.htm#QRn">QR-1</a>'.</p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Un">Un - max distance for a
|
||||
new, coplanar point </a></h3>
|
||||
|
||||
<p>This allows the user to set coplanarity. When pre-merging ('<a
|
||||
href="#Cn">C-n </a>', '<a href="#An">A-n</a>' or '<a
|
||||
href="qh-optq.htm#Qx">Qx</a>'), Qhull merges a new point into any
|
||||
coplanar facets. The default value for 'Un' is '<a href="#Vn">Vn</a>'.</p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Vn">Vn - min distance for a
|
||||
visible facet </a></h3>
|
||||
|
||||
<p>This allows the user to set facet visibility. When adding a
|
||||
point to the convex hull, Qhull determines all facets that are
|
||||
visible from the point. A facet is visible if the distance from
|
||||
the point to the facet is greater than 'Vn'.</p>
|
||||
|
||||
<p>Without merging, the default value for 'Vn' is the roundoff
|
||||
error ('<a href="#En">En</a>'). With merging, the default value
|
||||
is the pre-merge centrum ('<a href="#Cn">C-n</a>') in 2-d or 3-d,
|
||||
or three times that in other dimensions. If the outside width is
|
||||
specified with option '<a href="#Wn">Wn </a>', the maximum,
|
||||
default value for 'Vn' is '<a href="#Wn">Wn</a>'.</p>
|
||||
|
||||
<p>Qhull warns if 'Vn' is greater than '<a href="#Wn">Wn</a>' and
|
||||
furthest outside ('<a href="qh-optq.htm#Qf">Qf</a>') is not
|
||||
selected; this combination usually results in flipped facets
|
||||
(i.e., reversed normals).</p>
|
||||
|
||||
<h3><a href="#prec">»</a><a name="Wn">Wn - min distance above
|
||||
plane for outside points</a></h3>
|
||||
|
||||
<p>Points are added to the convex hull only if they are clearly
|
||||
outside of a facet. A point is outside of a facet if its distance
|
||||
to the facet is greater than 'Wn'. Without pre-merging, the
|
||||
default value for 'Wn' is '<a href="#En">En </a>'. If the user
|
||||
specifies pre-merging and does not set 'Wn', than 'Wn' is set to
|
||||
the maximum of '<a href="#Cn">C-n</a>' and <i>maxcoord*(1 - </i><a
|
||||
href="#An"><i>A-n</i></a><i>)</i>.</p>
|
||||
|
||||
<p>This option is good for <a href="qh-impre.htm#approximate">approximating</a>
|
||||
a convex hull.</p>
|
||||
|
||||
<p>Options '<a href="qh-optq.htm#Qc">Qc</a>' and '<a
|
||||
href="qh-optq.htm#Qi">Qi</a>' use the minimum vertex to
|
||||
distinguish coplanar points from interior points.</p>
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<!-- 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>
|
||||
736
xs/src/qhull/html/qh-optf.htm
Normal file
@@ -0,0 +1,736 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull format options (F)</title>
|
||||
</head>
|
||||
|
||||
<body><!-- Navigation links -->
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<hr>
|
||||
<!-- Main text of document -->
|
||||
<h1><a
|
||||
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/delaunay.html"><IMG
|
||||
align=middle alt=[delaunay] height=100
|
||||
src="qh--dt.gif" width=100 ></a> Qhull format options (F)</h1>
|
||||
|
||||
<p>This section lists the format options for Qhull. These options
|
||||
are indicated by 'F' followed by a letter. See <A
|
||||
href="qh-opto.htm#output" >Output</a>, <a href="qh-optp.htm#print">Print</a>,
|
||||
and <a href="qh-optg.htm#geomview">Geomview</a> for other output
|
||||
options. </p>
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="format">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Additional input & output formats</h2>
|
||||
|
||||
<p>These options allow for automatic processing of Qhull output.
|
||||
Options '<a href="qh-opto.htm#i">i</a>', '<a href="qh-opto.htm#o">o</a>',
|
||||
'<a href="qh-opto.htm#n">n</a>', and '<a href="qh-opto.htm#p">p</a>'
|
||||
may also be used.</p>
|
||||
|
||||
<dl compact>
|
||||
<dt>
|
||||
<dd><b>Summary and control</b>
|
||||
<dt><a href="#FA">FA</a>
|
||||
<dd>compute total area and volume for option '<A
|
||||
href="qh-opto.htm#s">s</a>'
|
||||
|
||||
<dt><a href="#FV">FV</a>
|
||||
<dd>print average vertex (interior point for '<A
|
||||
href="qhalf.htm">qhalf</a>')
|
||||
<dt><a href="#FQ">FQ</a>
|
||||
<dd>print command for qhull and input
|
||||
<dt><a href="#FO">FO</a>
|
||||
<dd>print options to stderr or stdout
|
||||
<dt><a href="#FS">FS</a>
|
||||
<dd>print sizes: total area and volume
|
||||
<dt><a href="#Fs">Fs</a>
|
||||
<dd>print summary: dim, #points, total vertices and
|
||||
facets, #vertices, #facets, max outer and inner plane
|
||||
<dt><a href="#Fd">Fd</a>
|
||||
<dd>use format for input (offset first)
|
||||
<dt><a href="#FD">FD</a>
|
||||
<dd>use cdd format for normals (offset first)
|
||||
<dt><a href="#FM">FM</a>
|
||||
<dd>print Maple output (2-d and 3-d)
|
||||
<dt>
|
||||
<dt>
|
||||
<dd><b>Facets, points, and vertices</b>
|
||||
<dt><a href="#Fa">Fa</a>
|
||||
<dd>print area for each facet
|
||||
<dt><a href="#FC">FC</a>
|
||||
<dd>print centrum for each facet
|
||||
<dt><a href="#Fc">Fc</a>
|
||||
<dd>print coplanar points for each facet
|
||||
<dt><a href="#Fx">Fx</a>
|
||||
<dd>print extreme points (i.e., vertices) of convex hull.
|
||||
|
||||
<dt><a href="#FF">FF</a>
|
||||
<dd>print facets w/o ridges
|
||||
<dt><a href="#FI">FI</a>
|
||||
<dd>print ID for each facet
|
||||
<dt><a href="#Fi">Fi</a>
|
||||
<dd>print inner planes for each facet
|
||||
<dt><a href="#Fm">Fm</a>
|
||||
<dd>print merge count for each facet (511 max)
|
||||
<dt><a href="#FP">FP</a>
|
||||
<dd>print nearest vertex for coplanar points
|
||||
<dt><a href="#Fn">Fn</a>
|
||||
<dd>print neighboring facets for each facet
|
||||
<dt><a href="#FN">FN</a>
|
||||
<dd>print neighboring facets for each point
|
||||
<dt><a href="#Fo">Fo</a>
|
||||
<dd>print outer planes for each facet
|
||||
<dt><a href="#Ft">Ft</a>
|
||||
<dd>print triangulation with added points
|
||||
<dt><a href="#Fv">Fv</a>
|
||||
<dd>print vertices for each facet
|
||||
<dt>
|
||||
<dt>
|
||||
<dd><b>Delaunay, Voronoi, and halfspace</b>
|
||||
<dt><a href="#Fx">Fx</a>
|
||||
<dd>print extreme input sites of Delaunay triangulation
|
||||
or Voronoi diagram.
|
||||
<dt><a href="#Fp">Fp</a>
|
||||
<dd>print points at halfspace intersections
|
||||
<dt><a href="#Fi2">Fi</a>
|
||||
<dd>print separating hyperplanes for inner, bounded
|
||||
Voronoi regions
|
||||
<dt><a href="#Fo2">Fo</a>
|
||||
<dd>print separating hyperplanes for outer, unbounded
|
||||
Voronoi regions
|
||||
<dt><a href="#Fv2">Fv</a>
|
||||
<dd>print Voronoi diagram as ridges for each input pair
|
||||
<dt><a href="#FC">FC</a>
|
||||
<dd>print Voronoi vertex ("center") for each facet</dd>
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fa">Fa - print area for each
|
||||
facet </a></h3>
|
||||
|
||||
<p>The first line is the number of facets. The remaining lines
|
||||
are the area for each facet, one facet per line. See '<A
|
||||
href="#FA" >FA</a>' and '<a href="#FS">FS</a>' for computing the total area and volume.</p>
|
||||
|
||||
<p>Use '<a href="qh-optp.htm#PAn">PAn</a>' for printing the n
|
||||
largest facets. Use option '<a href="qh-optp.htm#PFn">PFn</a>'
|
||||
for printing facets larger than <i>n</i>.</p>
|
||||
|
||||
<p>For Delaunay triangulations, the area is the area of each
|
||||
Delaunay triangle. For Voronoi vertices, the area is the area of
|
||||
the dual facet to each vertex. </p>
|
||||
|
||||
<p>Qhull uses the centrum and ridges to triangulate
|
||||
non-simplicial facets. The area for non-simplicial facets is the
|
||||
sum of the areas for each triangle. It is an approximation of the
|
||||
actual area. The ridge's vertices are projected to the facet's
|
||||
hyperplane. If a vertex is far below a facet (qh_WIDEcoplanar in <tt>user.h</tt>),
|
||||
the corresponding triangles are ignored.</p>
|
||||
|
||||
<p>For non-simplicial facets, vertices are often below the
|
||||
facet's hyperplane. If so, the approximation is less than the
|
||||
actual value and it may be significantly less. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FA">FA - compute total area
|
||||
and volume for option 's' </a></h3>
|
||||
|
||||
<p>With option 'FA', Qhull includes the total area and volume in
|
||||
the summary ('<a href="qh-opto.htm#s">s</a>'). Option '<a href="#FS">FS</a>' also includes the total area and volume.
|
||||
If facets are
|
||||
merged, the area and volume are approximations. Option 'FA' is
|
||||
automatically set for options '<a href="#Fa">Fa</a>', '<A
|
||||
href="qh-optp.htm#PAn" >PAn</a>', and '<a href="qh-optp.htm#PFn">PFn</a>'.
|
||||
</p>
|
||||
|
||||
<p>With '<a href="qdelaun.htm">qdelaunay</a> <A
|
||||
href="qh-opto.htm#s" >s</a> FA', Qhull computes the total area of
|
||||
the Delaunay triangulation. This equals the volume of the convex
|
||||
hull of the data points. With options '<a href="qdelau_f.htm">qdelaunay Qu</a>
|
||||
<a href="qh-opto.htm#s">s</a> FA', Qhull computes the
|
||||
total area of the furthest-site Delaunay triangulation. This
|
||||
equals of the total area of the Delaunay triangulation. </p>
|
||||
|
||||
<p>See '<a href="#Fa">Fa</a>' for further details. Option '<a href="#FS">FS</a>' also computes the total area and volume.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fc">Fc - print coplanar
|
||||
points for each facet </a></h3>
|
||||
|
||||
<p>The output starts with the number of facets. Then each facet
|
||||
is printed one per line. Each line is the number of coplanar
|
||||
points followed by the point ids. </p>
|
||||
|
||||
<p>By default, option 'Fc' reports coplanar points
|
||||
('<a href="qh-optq.htm#Qc">Qc</a>'). You may also use
|
||||
option '<a href="qh-optq.htm#Qi">Qi</a>'. Options 'Qi Fc' prints
|
||||
interior points while 'Qci Fc' prints both coplanar and interior
|
||||
points.
|
||||
|
||||
<p>Each coplanar point or interior point is assigned to the
|
||||
facet it is furthest above (resp., least below). </p>
|
||||
|
||||
<p>Use 'Qc <a href="qh-opto.htm#p">p</a>' to print vertex and
|
||||
coplanar point coordinates. Use '<a href="qh-optf.htm#Fv">Fv</a>'
|
||||
to print vertices. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FC">FC - print centrum or
|
||||
Voronoi vertex for each facet </a></h3>
|
||||
|
||||
<p>The output starts with the dimension followed by the number of
|
||||
facets. Then each facet centrum is printed, one per line. For
|
||||
<a href="qvoronoi.htm">qvoronoi</a>, Voronoi vertices are
|
||||
printed instead. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fd">Fd - use cdd format for
|
||||
input </a></h3>
|
||||
|
||||
<p>The input starts with comments. The first comment is reported
|
||||
in the summary. Data starts after a "begin" line. The
|
||||
next line is the number of points followed by the dimension plus
|
||||
one and "real" or "integer". Then the points
|
||||
are listed with a leading "1" or "1.0". The
|
||||
data ends with an "end" line.</p>
|
||||
|
||||
<p>For halfspaces ('<a href="qhalf.htm">qhalf</a> Fd'),
|
||||
the input format is the same. Each halfspace starts with its
|
||||
offset. The signs of the offset and coefficients are the
|
||||
opposite of Qhull's
|
||||
convention. The first two lines of the input may be an interior
|
||||
point in '<a href="#FV">FV</a>' format.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FD">FD - use cdd format for
|
||||
normals </a></h3>
|
||||
|
||||
<p>Option 'FD' prints normals ('<a href="qh-opto.htm#n">n</a>', '<A
|
||||
href="#Fo" >Fo</a>', '<a href="#Fi">Fi</a>') or points ('<A
|
||||
href="qh-opto.htm#p" >p</a>') in cdd format. The first line is the
|
||||
command line that invoked Qhull. Data starts with a
|
||||
"begin" line. The next line is the number of normals or
|
||||
points followed by the dimension plus one and "real".
|
||||
Then the normals or points are listed with the offset before the
|
||||
coefficients. The offset for points is 1.0. For normals,
|
||||
the offset and coefficients use the opposite sign from Qhull.
|
||||
The data ends with an "end" line.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FF">FF - print facets w/o
|
||||
ridges </a></h3>
|
||||
|
||||
<p>Option 'FF' prints all fields of all facets (as in '<A
|
||||
href="qh-opto.htm#f" >f</a>') without printing the ridges. This is
|
||||
useful in higher dimensions where a facet may have many ridges.
|
||||
For simplicial facets, options 'FF' and '<a href="qh-opto.htm#f">f
|
||||
</a>' are equivalent.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fi">Fi - print inner planes
|
||||
for each facet </a></h3>
|
||||
|
||||
<p>The first line is the dimension plus one. The second line is
|
||||
the number of facets. The remainder is one inner plane per line.
|
||||
The format is the same as option '<a href="qh-opto.htm#n">n</a>'.</p>
|
||||
|
||||
<p>The inner plane is a plane that is below the facet's vertices.
|
||||
It is an offset from the facet's hyperplane. It includes a
|
||||
roundoff error for computing the vertex distance.</p>
|
||||
|
||||
<p>Note that the inner planes for Geomview output ('<A
|
||||
href="qh-optg.htm#Gi" >Gi</a>') include an additional offset for
|
||||
vertex visualization and roundoff error. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fi2">Fi - print separating
|
||||
hyperplanes for inner, bounded Voronoi regions</a></h3>
|
||||
|
||||
<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fi' prints the
|
||||
separating hyperplanes for inner, bounded regions of the Voronoi
|
||||
diagram. The first line is the number of ridges. Then each
|
||||
hyperplane is printed, one per line. A line starts with the
|
||||
number of indices and floats. The first pair of indices indicates
|
||||
an adjacent pair of input sites. The next <i>d</i> floats are the
|
||||
normalized coefficients for the hyperplane, and the last float is
|
||||
the offset. The hyperplane is oriented toward '<A
|
||||
href="qh-optq.htm#QVn" >QVn</a>' (if defined), or the first input
|
||||
site of the pair. </p>
|
||||
|
||||
<p>Use '<a href="qh-optf.htm#Fo2">Fo</a>' for unbounded regions,
|
||||
and '<a href="qh-optf.htm#Fv2">Fv</a>' for the corresponding
|
||||
Voronoi vertices. </p>
|
||||
|
||||
<p>Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify that the
|
||||
hyperplanes are perpendicular bisectors. It will list relevant
|
||||
statistics to stderr. The hyperplane is a perpendicular bisector
|
||||
if the midpoint of the input sites lies on the plane, all Voronoi
|
||||
vertices in the ridge lie on the plane, and the angle between the
|
||||
input sites and the plane is ninety degrees. This is true if all
|
||||
statistics are zero. Roundoff and computation errors make these
|
||||
non-zero. The deviations appear to be largest when the
|
||||
corresponding Delaunay triangles are large and thin; for example,
|
||||
the Voronoi diagram of nearly cospherical points. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FI">FI - print ID for each
|
||||
facet </a></h3>
|
||||
|
||||
<p>Print facet identifiers. These are used internally and listed
|
||||
with options '<a href="qh-opto.htm#f">f</a>' and '<a href="#FF">FF</a>'.
|
||||
Options '<a href="#Fn">Fn </a>' and '<a href="#FN">FN</a>' use
|
||||
facet identifiers for negative indices. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fm">Fm - print merge count
|
||||
for each facet </a></h3>
|
||||
|
||||
<p>The first line is the number of facets. The remainder is the
|
||||
number of merges for each facet, one per line. At most 511 merges
|
||||
are reported for a facet. See '<a href="qh-optp.htm#PMn">PMn</a>'
|
||||
for printing the facets with the most merges. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FM">FM - print Maple
|
||||
output </a></h3>
|
||||
|
||||
<p>Qhull writes a Maple file for 2-d and 3-d convex hulls,
|
||||
2-d and 3-d halfspace intersections,
|
||||
and 2-d Delaunay triangulations. Qhull produces a 2-d
|
||||
or 3-d plot.
|
||||
|
||||
<p><i>Warning</i>: This option has not been tested in Maple.
|
||||
|
||||
<p>[From T. K. Abraham with help from M. R. Feinberg and N. Platinova.]
|
||||
The following steps apply while working within the
|
||||
Maple worksheet environment :
|
||||
<ol>
|
||||
<li>Generate the data and store it as an array . For example, in 3-d, data generated
|
||||
in Maple is of the form : x[i],y[i],z[i]
|
||||
<p>
|
||||
<li>Create a single variable and assign the entire array of data points to this variable.
|
||||
Use the "seq" command within square brackets as shown in the following example.
|
||||
(The square brackets are essential for the rest of the steps to work.)
|
||||
<p>
|
||||
>data:=[seq([x[i],y[i],z[i]],i=1..n)]:# here n is the number of data points
|
||||
|
||||
<li>Next we need to write the data to a file to be read by qhull. Before
|
||||
writing the data to a file, make sure that the qhull executable files and
|
||||
the data file lie in the same subdirectory. If the executable files are
|
||||
stored in the "C:\qhull3.1\" subdirectory, then save the file in the same
|
||||
subdirectory, say "C:\qhull3.1\datafile.txt". For the sake of integrity of
|
||||
the data file , it is best to first ensure that the data file does not
|
||||
exist before writing into the data file. This can be done by running a
|
||||
delete command first . To write the data to the file, use the "writedata"
|
||||
and the "writedata[APPEND]" commands as illustrated in the following example :
|
||||
<p>
|
||||
>system("del c:\\qhull3.1\\datafile.txt");#To erase any previous versions of the file
|
||||
<br>>writedata("c:\\qhull3.1\\datafile.txt ",[3, nops(data)]);#writing in qhull format
|
||||
<br>>writedata[APPEND]("c:\\ qhull3.1\\datafile.txt ", data);#writing the data points
|
||||
<li>
|
||||
Use the 'FM' option to produce Maple output. Store the output as a ".mpl" file.
|
||||
For example, using the file we created above, we type the following (in DOS environment)
|
||||
<p>
|
||||
qconvex s FM <datafile.txt >dataplot.mpl
|
||||
|
||||
<li>
|
||||
To read 3-d output in Maple, we use the 'read' command followed by
|
||||
a 'display3d' command. For example (in Maple environment):
|
||||
<p>
|
||||
>with (plots):
|
||||
<br>>read `c:\\qhull3.1\\dataplot.mpl`:#IMPORTANT - Note that the punctuation mark used is ' and NOT '. The correct punctuation mark is the one next to the key for "1" (not the punctuation mark near the enter key)
|
||||
<br>> qhullplot:=%:
|
||||
<br>> display3d(qhullplot);
|
||||
</ol>
|
||||
|
||||
<p>For Delaunay triangulation orthogonal projection is better.
|
||||
|
||||
<p>For halfspace intersections, Qhull produces the dual
|
||||
convex hull.
|
||||
|
||||
<p>See <a href="qh-faq.htm#math">Is Qhull available for Maple?</a>
|
||||
for other URLs.
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fn">Fn - print neighboring
|
||||
facets for each facet </a></h3>
|
||||
|
||||
<p>The output starts with the number of facets. Then each facet
|
||||
is printed one per line. Each line is the number of neighbors
|
||||
followed by an index for each neighbor. The indices match the
|
||||
other facet output formats.</p>
|
||||
|
||||
<p>For simplicial facets, each neighbor is opposite
|
||||
the corresponding vertex (option '<a href="#Fv">Fv</a>').
|
||||
Do not compare to option '<a href="qh-opto.htm#i">i</a>'. Option 'i'
|
||||
orients facets by reversing the order of two vertices. For non-simplicial facets,
|
||||
the neighbors are unordered.
|
||||
|
||||
<p>A negative index indicates an unprinted facet due to printing
|
||||
only good facets ('<a href="qh-optp.htm#Pg">Pg</a>', <a href="qdelaun.htm" >qdelaunay</a>,
|
||||
<a href="qvoronoi.htm" >qvoronoi</a>). It
|
||||
is the negation of the facet's ID (option '<a href="#FI">FI</a>').
|
||||
For example, negative indices are used for facets "at
|
||||
infinity" in the Delaunay triangulation.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FN">FN - print neighboring
|
||||
facets for each point </a></h3>
|
||||
|
||||
<p>The first line is the number of points. Then each point is
|
||||
printed, one per line. For unassigned points (either interior or
|
||||
coplanar), the line is "0". For assigned coplanar
|
||||
points ('<a href="qh-optq.htm#Qc">Qc</a>'), the line is
|
||||
"1" followed by the index of the facet that is furthest
|
||||
below the point. For assigned interior points ('<A
|
||||
href="qh-optq.htm#Qi" >Qi</a>'), the line is "1"
|
||||
followed by the index of the facet that is least above the point.
|
||||
For vertices that do not belong to good facet, the line is
|
||||
"0"</p>
|
||||
|
||||
<p>For vertices of good facets, the line is the number of
|
||||
neighboring facets followed by the facet indices. The indices
|
||||
correspond to the other '<a href="#format">F</a>' formats. In 4-d
|
||||
and higher, the facets are sorted by index. In 3-d, the facets
|
||||
are in adjacency order (not oriented).</p>
|
||||
|
||||
<p>A negative index indicates an unprinted facet due to printing
|
||||
only good facets (<a href="qdelaun.htm" >qdelaunay</a>,
|
||||
<a href="qvoronoi.htm" >qvoronoi</a>, '<a href="qh-optp.htm#Pdk">Pdk</a>',
|
||||
'<a href="qh-optp.htm#Pg">Pg</a>'). It is the negation of the
|
||||
facet's ID ('<a href="#FI"> FI</a>'). For example, negative
|
||||
indices are used for facets "at infinity" in the
|
||||
Delaunay triangulation.</p>
|
||||
|
||||
<p>For Voronoi vertices, option 'FN' lists the vertices of the
|
||||
Voronoi region for each input site. Option 'FN' lists the regions
|
||||
in site ID order. Option 'FN' corresponds to the second half of
|
||||
option '<a href="qh-opto.htm#o">o</a>'. To convert from 'FN' to '<A
|
||||
href="qh-opto.htm#o" >o</a>', replace negative indices with zero
|
||||
and increment non-negative indices by one. </p>
|
||||
|
||||
<p>If you are using the <a href="qh-code.htm#library">Qhull
|
||||
library</a> or <a href="qh-code.htm#cpp">C++ interface</a>, option 'FN' has the side effect of reordering the
|
||||
neighbors for a vertex </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fo">Fo - print outer planes
|
||||
for each facet </a></h3>
|
||||
|
||||
<p>The first line is the dimension plus one. The second line is
|
||||
the number of facets. The remainder is one outer plane per line.
|
||||
The format is the same as option '<a href="qh-opto.htm#n">n</a>'.</p>
|
||||
|
||||
<p>The outer plane is a plane that is above all points. It is an
|
||||
offset from the facet's hyperplane. It includes a roundoff error
|
||||
for computing the point distance. When testing the outer plane
|
||||
(e.g., '<a href="qh-optt.htm#Tv">Tv</a>'), another roundoff error
|
||||
should be added for the tested point.</p>
|
||||
|
||||
<p>If outer planes are not checked ('<a href="qh-optq.htm#Q5">Q5</a>')
|
||||
or not computed (!qh_MAXoutside), the maximum, computed outside
|
||||
distance is used instead. This can be much larger than the actual
|
||||
outer planes.</p>
|
||||
|
||||
<p>Note that the outer planes for Geomview output ('<A
|
||||
href="qh-optg.htm#G" >G</a>') include an additional offset for
|
||||
vertex/point visualization, 'lines closer,' and roundoff error.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fo2">Fo - print separating
|
||||
hyperplanes for outer, unbounded Voronoi regions</a></h3>
|
||||
|
||||
<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fo' prints the
|
||||
separating hyperplanes for outer, unbounded regions of the
|
||||
Voronoi diagram. The first line is the number of ridges. Then
|
||||
each hyperplane is printed, one per line. A line starts with the
|
||||
number of indices and floats. The first pair of indices indicates
|
||||
an adjacent pair of input sites. The next <i>d</i> floats are the
|
||||
normalized coefficients for the hyperplane, and the last float is
|
||||
the offset. The hyperplane is oriented toward '<A
|
||||
href="qh-optq.htm#QVn" >QVn</a>' (if defined), or the first input
|
||||
site of the pair. </p>
|
||||
|
||||
<p>Option 'Fo' gives the hyperplanes for the unbounded rays of
|
||||
the unbounded regions of the Voronoi diagram. Each hyperplane
|
||||
goes through the midpoint of the corresponding input sites. The
|
||||
rays are directed away from the input sites. </p>
|
||||
|
||||
<p>Use '<a href="qh-optf.htm#Fi2">Fi</a>' for bounded regions,
|
||||
and '<a href="qh-optf.htm#Fv2">Fv</a>' for the corresponding
|
||||
Voronoi vertices. Use '<a href="qh-optt.htm#Tv">Tv</a>' to verify
|
||||
that the corresponding Voronoi vertices lie on the hyperplane. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FO">FO - print list of
|
||||
selected options </a></h3>
|
||||
|
||||
<p>Lists selected options and default values to stderr.
|
||||
Additional 'FO's are printed to stdout. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fp">Fp - print points at
|
||||
halfspace intersections</a></h3>
|
||||
|
||||
<p>The first line is the number of intersection points. The
|
||||
remainder is one intersection point per line. A intersection
|
||||
point is the intersection of <i>d</i> or more halfspaces from
|
||||
'<a href="qhalf.htm">qhalf</a>'. It corresponds to a
|
||||
facet of the dual polytope. The "infinity" point
|
||||
[-10.101,-10.101,...] indicates an unbounded intersection.</p>
|
||||
|
||||
<p>If [x,y,z] are the dual facet's normal coefficients and <i>b<0</i>
|
||||
is its offset, the halfspace intersection occurs at
|
||||
[x/-b,y/-b,z/-b] plus the interior point. If <i>b>=0</i>, the
|
||||
halfspace intersection is unbounded. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FP">FP - print nearest
|
||||
vertex for coplanar points </a></h3>
|
||||
|
||||
<p>The output starts with the number of coplanar points. Then
|
||||
each coplanar point is printed one per line. Each line is the
|
||||
point ID of the closest vertex, the point ID of the coplanar
|
||||
point, the corresponding facet ID, and the distance. Sort the
|
||||
lines to list the coplanar points nearest to each vertex. </p>
|
||||
|
||||
<p>Use options '<a href="qh-optq.htm#Qc">Qc</a>' and/or '<A
|
||||
href="qh-optq.htm#Qi" >Qi</a>' with 'FP'. Options 'Qc FP' prints
|
||||
coplanar points while 'Qci FP' prints coplanar and interior
|
||||
points. Option 'Qc' is automatically selected if 'Qi' is not
|
||||
selected.
|
||||
|
||||
<p>For Delaunay triangulations (<a href="qdelaun.htm" >qdelaunay</a>
|
||||
or <a href="qvoronoi.htm" >qvoronoi</a>), a coplanar point is nearly
|
||||
incident to a vertex. The distance is the distance in the
|
||||
original point set.</p>
|
||||
|
||||
<p>If imprecision problems are severe, Qhull will delete input
|
||||
sites when constructing the Delaunay triangulation. Option 'FP' will
|
||||
list these points along with coincident points.</p>
|
||||
|
||||
<p>If there are many coplanar or coincident points and non-simplicial
|
||||
facets are triangulated ('<a href="qh-optq.htm#Qt">Qt</a>'), option
|
||||
'FP' may be inefficient. It redetermines the original vertex set
|
||||
for each coplanar point.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FQ">FQ - print command for
|
||||
qhull and input </a></h3>
|
||||
|
||||
<p>Prints qhull and input command, e.g., "rbox 10 s | qhull
|
||||
FQ". Option 'FQ' may be repeated multiple times.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fs">Fs - print summary</a></h3>
|
||||
|
||||
<p>The first line consists of number of integers ("10")
|
||||
followed by the:
|
||||
<ul>
|
||||
<li>dimension
|
||||
<li>number of points
|
||||
<li>number of vertices
|
||||
<li>number of facets
|
||||
<li>number of vertices selected for output
|
||||
<li>number of facets selected for output
|
||||
<li>number of coplanar points for selected facets
|
||||
<li>number of nonsimplicial or merged facets selected for
|
||||
output
|
||||
<LI>number of deleted vertices</LI>
|
||||
<LI>number of triangulated facets ('<a href="qh-optq.htm#Qt">Qt</a>')</LI>
|
||||
</ul>
|
||||
|
||||
<p>The second line consists of the number of reals
|
||||
("2") followed by the:
|
||||
<ul>
|
||||
<li>maximum offset to an outer plane
|
||||
<li>minimum offset to an inner plane.</li>
|
||||
</ul>
|
||||
Roundoff and joggle are included.
|
||||
<P></P>
|
||||
|
||||
<p>For Delaunay triangulations and Voronoi diagrams, the
|
||||
number of deleted vertices should be zero. If greater than zero, then the
|
||||
input is highly degenerate and coplanar points are not necessarily coincident
|
||||
points. For example, <tt>'RBOX 1000 s W1e-13 t995138628 | QHULL d Qbb'</tt> reports
|
||||
deleted vertices; the input is nearly cospherical.</p>
|
||||
|
||||
<P>Later versions of Qhull may produce additional integers or reals.</P>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FS">FS - print sizes</a></h3>
|
||||
|
||||
<p>The first line consists of the number of integers
|
||||
("0"). The second line consists of the number of reals
|
||||
("2"), followed by the total facet area, and the total
|
||||
volume. Later versions of Qhull may produce additional integers
|
||||
or reals.</p>
|
||||
|
||||
<p>The total volume measures the volume of the intersection of
|
||||
the halfspaces defined by each facet. It is computed from the
|
||||
facet area. Both area and volume are approximations for
|
||||
non-simplicial facets. See option '<a href="#Fa">Fa </a>' for
|
||||
further notes. Option '<a href="#FA">FA </a>' also computes the total area and volume. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Ft">Ft - print triangulation</a></h3>
|
||||
|
||||
<p>Prints a triangulation with added points for non-simplicial
|
||||
facets. The output is </p>
|
||||
|
||||
<ul>
|
||||
<li>The first line is the dimension
|
||||
<li>The second line is the number of points, the number
|
||||
of facets, and the number of ridges.
|
||||
<li>All of the input points follow, one per line.
|
||||
<li>The centrums follow, one per non-simplicial facet
|
||||
<li>Then the facets follow as a list of point indices
|
||||
preceded by the number of points. The simplices are
|
||||
oriented. </li>
|
||||
</ul>
|
||||
|
||||
<p>For convex hulls with simplicial facets, the output is the
|
||||
same as option '<a href="qh-opto.htm#o">o</a>'.</p>
|
||||
|
||||
<p>The added points are the centrums of the non-simplicial
|
||||
facets. Except for large facets, the centrum is the average
|
||||
vertex coordinate projected to the facet's hyperplane. Large
|
||||
facets may use an old centrum to avoid recomputing the centrum
|
||||
after each merge. In either case, the centrum is clearly below
|
||||
neighboring facets. See <a href="qh-impre.htm">Precision issues</a>.
|
||||
</p>
|
||||
|
||||
<p>The new simplices will not be clearly convex with their
|
||||
neighbors and they will not satisfy the Delaunay property. They
|
||||
may even have a flipped orientation. Use triangulated input ('<A
|
||||
href="qh-optq.htm#Qt">Qt</a>') for Delaunay triangulations.
|
||||
|
||||
<p>For Delaunay triangulations with simplicial facets, the output is the
|
||||
same as option '<a href="qh-opto.htm#o">o</a>' without the lifted
|
||||
coordinate. Since 'Ft' is invalid for merged Delaunay facets, option
|
||||
'Ft' is not available for qdelaunay or qvoronoi. It may be used with
|
||||
joggled input ('<a href="qh-optq.htm#QJn" >QJ</a>') or triangulated output ('<A
|
||||
href="qh-optq.htm#Qt" >Qt</a>'), for example, rbox 10 c G 0.01 | qhull d QJ Ft</p>
|
||||
|
||||
<p>If you add a point-at-infinity with '<a href="qh-optq.htm#Qz">Qz</a>',
|
||||
it is printed after the input sites and before any centrums. It
|
||||
will not be used in a Delaunay facet.</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fv">Fv - print vertices for
|
||||
each facet</a></h3>
|
||||
|
||||
<p>The first line is the number of facets. Then each facet is
|
||||
printed, one per line. Each line is the number of vertices
|
||||
followed by the corresponding point ids. Vertices are listed in
|
||||
the order they were added to the hull (the last one added is the
|
||||
first listed).
|
||||
</p>
|
||||
<p>Option '<a href="qh-opto.htm#i">i</a>' also lists the vertices,
|
||||
but it orients facets by reversing the order of two
|
||||
vertices. Option 'i' triangulates non-simplicial, 4-d and higher facets by
|
||||
adding vertices for the centrums.
|
||||
</p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fv2">Fv - print Voronoi
|
||||
diagram</a></h3>
|
||||
|
||||
<p>With <a href="qvoronoi.htm" >qvoronoi</a>, 'Fv' prints the
|
||||
Voronoi diagram or furthest-site Voronoi diagram. The first line
|
||||
is the number of ridges. Then each ridge is printed, one per
|
||||
line. The first number is the count of indices. The second pair
|
||||
of indices indicates a pair of input sites. The remaining indices
|
||||
list the corresponding ridge of Voronoi vertices. Vertex 0 is the
|
||||
vertex-at-infinity. It indicates an unbounded ray. </p>
|
||||
|
||||
<p>All vertices of a ridge are coplanar. If the ridge is
|
||||
unbounded, add the midpoint of the pair of input sites. The
|
||||
unbounded ray is directed from the Voronoi vertices to infinity. </p>
|
||||
|
||||
<p>Use '<a href="qh-optf.htm#Fo2">Fo</a>' for separating
|
||||
hyperplanes of outer, unbounded regions. Use '<A
|
||||
href="qh-optf.htm#Fi2" >Fi</a>' for separating hyperplanes of
|
||||
inner, bounded regions. </p>
|
||||
|
||||
<p>Option 'Fv' does not list ridges that require more than one
|
||||
midpoint. For example, the Voronoi diagram of cospherical points
|
||||
lists zero ridges (e.g., 'rbox 10 s | qvoronoi Fv Qz').
|
||||
Other examples are the Voronoi diagrams of a rectangular mesh
|
||||
(e.g., 'rbox 27 M1,0 | qvoronoi Fv') or a point set with
|
||||
a rectangular corner (e.g.,
|
||||
'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qvoronoi Fv').
|
||||
Both cases miss unbounded rays at the corners.
|
||||
To determine these ridges, surround the points with a
|
||||
large cube (e.g., 'rbox 10 s c G2.0 | qvoronoi Fv Qz').
|
||||
The cube needs to be large enough to bound all Voronoi regions of the original point set.
|
||||
Please report any other cases that are missed. If you
|
||||
can formally describe these cases or
|
||||
write code to handle them, please send email to <A
|
||||
href="mailto:qhull@qhull.org" >qhull@qhull.org</a>. </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="FV">FV - print average
|
||||
vertex </a></h3>
|
||||
|
||||
<p>The average vertex is the average of all vertex coordinates.
|
||||
It is an interior point for halfspace intersection. The first
|
||||
line is the dimension and "1"; the second line is the
|
||||
coordinates. For example,</p>
|
||||
|
||||
<blockquote>
|
||||
<p>qconvex FV <A
|
||||
href="qh-opto.htm#n">n</a> | qhalf <a href="#Fp">Fp</a></p>
|
||||
</blockquote>
|
||||
|
||||
<p>prints the extreme points of the original point set (roundoff
|
||||
included). </p>
|
||||
|
||||
<h3><a href="#format">»</a><a name="Fx">Fx - print extreme
|
||||
points (vertices) of convex hulls and Delaunay triangulations</a></h3>
|
||||
|
||||
<p>The first line is the number of points. The following lines
|
||||
give the index of the corresponding points. The first point is
|
||||
'0'. </p>
|
||||
|
||||
<p>In 2-d, the extreme points (vertices) are listed in
|
||||
counterclockwise order (by qh_ORIENTclock in user.h). </p>
|
||||
|
||||
<p>In 3-d and higher convex hulls, the extreme points (vertices)
|
||||
are sorted by index. This is the same order as option '<A
|
||||
href="qh-opto.htm#p" >p</a>' when it doesn't include coplanar or
|
||||
interior points. </p>
|
||||
|
||||
<p>For Delaunay triangulations, 'Fx' lists the extreme
|
||||
points of the input sites (i.e., the vertices of their convex hull). The points
|
||||
are unordered. <!-- Navigation links --> </p>
|
||||
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p><!-- GC common information -->
|
||||
<hr>
|
||||
|
||||
<p><a href="http://www.geom.uiuc.edu/"><IMG align=middle
|
||||
height=40 src="qh--geom.gif" width=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>
|
||||
274
xs/src/qhull/html/qh-optg.htm
Normal file
@@ -0,0 +1,274 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull Geomview options (G)</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<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> Qhull Geomview options (G)</h1>
|
||||
|
||||
This section lists the Geomview options for Qhull. These options are
|
||||
indicated by 'G' followed by a letter. See
|
||||
<a href="qh-opto.htm#output">Output</a>, <a href="qh-optp.htm#print">Print</a>,
|
||||
and <a href="qh-optf.htm#format">Format</a> for other output options.
|
||||
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="geomview">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Geomview output options</h2>
|
||||
|
||||
<p><a href="http://www.geomview.org">Geomview</a> is the graphical
|
||||
viewer for visualizing Qhull output in 2-d, 3-d and 4-d.</p>
|
||||
|
||||
<p>Geomview displays each facet of the convex hull. The color of
|
||||
a facet is determined by the coefficients of the facet's normal
|
||||
equation. For imprecise hulls, Geomview displays the inner and
|
||||
outer hull. Geomview can also display points, ridges, vertices,
|
||||
coplanar points, and facet intersections. </p>
|
||||
|
||||
<p>For 2-d Delaunay triangulations, Geomview displays the
|
||||
corresponding paraboloid. Geomview displays the 2-d Voronoi
|
||||
diagram. For halfspace intersections, it displays the
|
||||
dual convex hull. </p>
|
||||
|
||||
<dl compact>
|
||||
<dt> </dt>
|
||||
<dd><b>General</b></dd>
|
||||
<dt><a href="#G">G</a></dt>
|
||||
<dd>display Geomview output</dd>
|
||||
<dt><a href="#Gt">Gt</a></dt>
|
||||
<dd>display transparent 3-d Delaunay triangulation</dd>
|
||||
<dt><a href="#GDn">GDn</a></dt>
|
||||
<dd>drop dimension n in 3-d and 4-d output </dd>
|
||||
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Specific</b></dd>
|
||||
<dt><a href="#Ga">Ga</a></dt>
|
||||
<dd>display all points as dots</dd>
|
||||
<dt><a href="#Gc">Gc</a></dt>
|
||||
<dd>display centrums (2-d, 3-d)</dd>
|
||||
<dt><a href="#Gp">Gp</a></dt>
|
||||
<dd>display coplanar points and vertices as radii</dd>
|
||||
<dt><a href="#Gh">Gh</a></dt>
|
||||
<dd>display hyperplane intersections</dd>
|
||||
<dt><a href="#Gi">Gi</a></dt>
|
||||
<dd>display inner planes only (2-d, 3-d)</dd>
|
||||
<dt><a href="#Go">Go</a></dt>
|
||||
<dd>display outer planes only (2-d, 3-d)</dd>
|
||||
<dt><a href="#Gr">Gr</a></dt>
|
||||
<dd>display ridges (3-d)</dd>
|
||||
<dt><a href="#Gv">Gv</a></dt>
|
||||
<dd>display vertices as spheres</dd>
|
||||
<dt><a href="#Gn">Gn</a></dt>
|
||||
<dd>do not display planes</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="G">G - produce output for
|
||||
viewing with Geomview</a></h3>
|
||||
|
||||
<p>By default, option 'G' displays edges in 2-d, outer planes in
|
||||
3-d, and ridges in 4-d.</p>
|
||||
|
||||
<p>A ridge can be explicit or implicit. An explicit ridge is a <i>(d-1)</i>-dimensional
|
||||
simplex between two facets. In 4-d, the explicit ridges are
|
||||
triangles. An implicit ridge is the topological intersection of
|
||||
two neighboring facets. It is the union of explicit ridges.</p>
|
||||
|
||||
<p>For non-simplicial 4-d facets, the explicit ridges can be
|
||||
quite complex. When displaying a ridge in 4-d, Qhull projects the
|
||||
ridge's vertices to one of its facets' hyperplanes. Use '<a
|
||||
href="#Gh">Gh</a>' to project ridges to the intersection of both
|
||||
hyperplanes. This usually results in a cleaner display. </p>
|
||||
|
||||
<p>For 2-d Delaunay triangulations, Geomview displays the
|
||||
corresponding paraboloid. Geomview displays the 2-d Voronoi
|
||||
diagram. For halfspace intersections, it displays the
|
||||
dual convex hull.
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Ga">Ga - display all
|
||||
points as dots </a></h3>
|
||||
|
||||
<p>Each input point is displayed as a green dot.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gc">Gc - display centrums
|
||||
(3-d) </a></h3>
|
||||
|
||||
<p>The centrum is defined by a green radius sitting on a blue
|
||||
plane. The plane corresponds to the facet's hyperplane. If you
|
||||
sight along a facet's hyperplane, you will see that all
|
||||
neighboring centrums are below the facet. The radius is defined
|
||||
by '<a href="qh-optc.htm#Cn">C-n</a>' or '<a
|
||||
href="qh-optc.htm#Cn2">Cn</a>'.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="GDn">GDn - drop dimension
|
||||
n in 3-d and 4-d output </a></h3>
|
||||
|
||||
<p>The result is a 2-d or 3-d object. In 4-d, this corresponds to
|
||||
viewing the 4-d object from the nth axis without perspective.
|
||||
It's best to view 4-d objects in pieces. Use the '<a
|
||||
href="qh-optp.htm#Pdk">Pdk</a>' '<a href="qh-optp.htm#Pg">Pg</a>'
|
||||
'<a href="qh-optp.htm#PG">PG</a>' '<a href="qh-optq.htm#QGn">QGn</a>'
|
||||
and '<a href="qh-optq.htm#QVn">QVn</a>' options to select a few
|
||||
facets. If one of the facets is perpendicular to an axis, then
|
||||
projecting along that axis will show the facet exactly as it is
|
||||
in 4-d. If you generate many facets, use Geomview's <tt>ginsu</tt>
|
||||
module to view the interior</p>
|
||||
|
||||
<p>To view multiple 4-d dimensions at once, output the object
|
||||
without 'GDn' and read it with Geomview's <tt>ndview</tt>. As you
|
||||
rotate the object in one set of dimensions, you can see how it
|
||||
changes in other sets of dimensions.</p>
|
||||
|
||||
<p>For additional control over 4-d objects, output the object
|
||||
without 'GDn' and read it with Geomview's <tt>4dview</tt>. You
|
||||
can slice the object along any 4-d plane. You can also flip the
|
||||
halfspace that's deleted when slicing. By combining these
|
||||
features, you can get some interesting cross sections.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gh">Gh - display
|
||||
hyperplane intersections (3-d, 4-d)</a></h3>
|
||||
|
||||
<p>In 3-d, the intersection is a black line. It lies on two
|
||||
neighboring hyperplanes, c.f., the blue squares associated with
|
||||
centrums ('<a href="#Gc">Gc </a>'). In 4-d, the ridges are
|
||||
projected to the intersection of both hyperplanes. If you turn on
|
||||
edges (Geomview's 'appearances' menu), each triangle corresponds
|
||||
to one ridge. The ridges may overlap each other.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gi">Gi - display inner
|
||||
planes only (2-d, 3-d)</a></h3>
|
||||
|
||||
<p>The inner plane of a facet is below all of its vertices. It is
|
||||
parallel to the facet's hyperplane. The inner plane's color is
|
||||
the opposite of the outer plane's color, i.e., <i>[1-r,1-g,1-b] </i>.
|
||||
Its edges are determined by the vertices.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gn">Gn - do not display
|
||||
planes </a></h3>
|
||||
|
||||
<p>By default, Geomview displays the precise plane (no merging)
|
||||
or both inner and output planes (if merging). If merging,
|
||||
Geomview does not display the inner plane if the the difference
|
||||
between inner and outer is too small.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Go">Go - display outer
|
||||
planes only (2-d, 3-d)</a></h3>
|
||||
|
||||
<p>The outer plane of a facet is above all input points. It is
|
||||
parallel to the facet's hyperplane. Its color is determined by
|
||||
the facet's normal, and its edges are determined by the vertices.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gp">Gp - display coplanar
|
||||
points and vertices as radii </a></h3>
|
||||
|
||||
<p>Coplanar points ('<a href="qh-optq.htm#Qc">Qc</a>'), interior
|
||||
points ('<a href="qh-optq.htm#Qi">Qi</a>'), outside points ('<a
|
||||
href="qh-optt.htm#TCn">TCn</a>' or '<a href="qh-optt.htm#TVn">TVn</a>'),
|
||||
and vertices are displayed as red and yellow radii. The radii are
|
||||
perpendicular to the corresponding facet. Vertices are aligned
|
||||
with an interior point. The radii define a ball which corresponds
|
||||
to the imprecision of the point. The imprecision is the maximum
|
||||
of the roundoff error, the centrum radius, and <i>maxcoord * (1 -
|
||||
</i><a href="qh-optc.htm#An"><i>A-n</i></a><i>)</i>. It is at
|
||||
least 1/20'th of the maximum coordinate, and ignores post merging
|
||||
if pre-merging is done.</p>
|
||||
|
||||
<p>If '<a href="qh-optg.htm#Gv">Gv</a>' (print vertices as
|
||||
spheres) is also selected, option 'Gp' displays coplanar
|
||||
points as radii. Select options <a href="qh-optq.htm#Qc">Qc</a>'
|
||||
and/or '<a href="qh-optq.htm#Qi">Qi</a>'. Options 'Qc Gpv' displays
|
||||
coplanar points while 'Qci Gpv' displays coplanar and interior
|
||||
points. Option 'Qc' is automatically selected if 'Qi' is not
|
||||
selected with options 'Gpv'.
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gr">Gr - display ridges
|
||||
(3-d) </a></h3>
|
||||
|
||||
<p>A ridge connects the two vertices that are shared by
|
||||
neighboring facets. It is displayed in green. A ridge is the
|
||||
topological edge between two facets while the hyperplane
|
||||
intersection is the geometric edge between two facets. Ridges are
|
||||
always displayed in 4-d.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gt">Gt - transparent 3-d
|
||||
Delaunay </a></h3>
|
||||
|
||||
<p>A 3-d Delaunay triangulation looks like a convex hull with
|
||||
interior facets. Option 'Gt' removes the outside ridges to reveal
|
||||
the outermost facets. It automatically sets options '<a
|
||||
href="#Gr">Gr</a>' and '<a href="#GDn">GDn</a>'. See example <a
|
||||
href="qh-eg.htm#17f">eg.17f.delaunay.3</a>.</p>
|
||||
|
||||
<h3><a href="#geomview">»</a><a name="Gv">Gv - display vertices
|
||||
as spheres (2-d, 3-d)</a></h3>
|
||||
|
||||
<p>The radius of the sphere corresponds to the imprecision of the
|
||||
data. See '<a href="#Gp">Gp</a>' for determining the radius.</p>
|
||||
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<!-- 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>
|
||||
353
xs/src/qhull/html/qh-opto.htm
Normal file
@@ -0,0 +1,353 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull output options</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<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> Qhull output options</h1>
|
||||
|
||||
<p>This section lists the output options for Qhull. These options
|
||||
are indicated by lower case characters. See <a
|
||||
href="qh-optf.htm#format">Formats</a>, <a
|
||||
href="qh-optp.htm#print">Print</a>, and <a
|
||||
href="qh-optg.htm#geomview">Geomview</a> for other output
|
||||
options. </p>
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="output">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Output options</h2>
|
||||
|
||||
<p>Qhull prints its output to standard out. All output is printed
|
||||
text. The default output is a summary (option '<a href="#s">s</a>').
|
||||
Other outputs may be specified as follows. </p>
|
||||
|
||||
<dl compact>
|
||||
<dt><a href="#f">f</a></dt>
|
||||
<dd>print all fields of all facets</dd>
|
||||
<dt><a href="#n">n</a></dt>
|
||||
<dd>print hyperplane normals with offsets</dd>
|
||||
<dt><a href="#m">m</a></dt>
|
||||
<dd>print Mathematica output (2-d and 3-d)</dd>
|
||||
<dt><a href="#o">o</a></dt>
|
||||
<dd>print OFF file format (dim, points and facets)</dd>
|
||||
<dt><a href="#s">s</a></dt>
|
||||
<dd>print summary to stderr</dd>
|
||||
<dt><a href="#p">p</a></dt>
|
||||
<dd>print vertex and point coordinates</dd>
|
||||
<dt><a href="#i">i</a></dt>
|
||||
<dd>print vertices incident to each facet </dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Related options</b></dd>
|
||||
<dt><a href="qh-optf.htm#format">F</a></dt>
|
||||
<dd>additional input/output formats</dd>
|
||||
<dt><a href="qh-optg.htm#geomview">G</a></dt>
|
||||
<dd>Geomview output</dd>
|
||||
<dt><a href="qh-optp.htm#print">P</a></dt>
|
||||
<dd>Print options</dd>
|
||||
<dt><a href="qh-optf.htm#Ft">Ft</a></dt>
|
||||
<dd>print triangulation with added points</dd>
|
||||
<dt> </dt>
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#output">»</a><a name="f">f - print all fields of
|
||||
all facets </a></h3>
|
||||
|
||||
<p>Print <a href=../src/libqhull.h#facetT>all fields</a> of all facets.
|
||||
The facet is the primary <a href=index.htm#structure>data structure</a> for
|
||||
Qhull.
|
||||
|
||||
<p>Option 'f' is for
|
||||
debugging. Most of the fields are available via the '<a
|
||||
href="qh-optf.htm#format">F</a>' options. If you need specialized
|
||||
information from Qhull, you can use the <a
|
||||
href="qh-code.htm#library">Qhull library</a> or <a
|
||||
href="qh-code.htm#cpp">C++ interface</a>.</p>
|
||||
|
||||
<p>Use the '<a href="qh-optf.htm#FF">FF</a>' option to print the
|
||||
facets but not the ridges. </p>
|
||||
|
||||
<h3><a href="#output">»</a><a name="i">i - print vertices
|
||||
incident to each facet </a></h3>
|
||||
|
||||
<p>The first line is the number of facets. The remaining lines
|
||||
list the vertices for each facet, one facet per line. The indices
|
||||
are 0-relative indices of the corresponding input points. The
|
||||
facets are oriented. Option '<a href="qh-optf.htm#Fv">Fv</a>'
|
||||
displays an unoriented list of vertices with a vertex count per
|
||||
line. Options '<a href="qh-opto.htm#o">o</a>' and '<a
|
||||
href="qh-optf.htm#Ft">Ft</a>' displays coordinates for each
|
||||
vertex prior to the vertices for each facet. </p>
|
||||
|
||||
<p>Simplicial facets (e.g., triangles in 3-d) consist of <i>d</i>
|
||||
vertices. Non-simplicial facets in 3-d consist of 4 or more
|
||||
vertices. For example, a facet of a cube consists of 4 vertices.
|
||||
Use option '<a href="qh-optq.htm#Qt">Qt</a>' to triangulate non-simplicial facets.</p>
|
||||
|
||||
<p>For 4-d and higher convex hulls and 3-d and higher Delaunay
|
||||
triangulations, <i>d</i> vertices are listed for all facets. A
|
||||
non-simplicial facet is triangulated with its centrum and each
|
||||
ridge. The index of the centrum is higher than any input point.
|
||||
Use option '<a href="qh-optf.htm#Fv">Fv</a>' to list the vertices
|
||||
of non-simplicial facets as is. Use option '<a
|
||||
href="qh-optf.htm#Ft">Ft</a>' to print the coordinates of the
|
||||
centrums as well as those of the input points. </p>
|
||||
|
||||
<h3><a href="#output">»</a><a name="m">m - print Mathematica
|
||||
output </a></h3>
|
||||
|
||||
<p>Qhull writes a Mathematica file for 2-d and 3-d convex hulls,
|
||||
2-d and 3-d halfspace intersections,
|
||||
and 2-d Delaunay triangulations. Qhull produces a list of
|
||||
objects that you can assign to a variable in Mathematica, for
|
||||
example: "<tt>list= << <outputfilename> </tt>".
|
||||
If the object is 2-d, it can be visualized by "<tt>Show[Graphics[list]]
|
||||
</tt>". For 3-d objects the command is "<tt>Show[Graphics3D[list]]
|
||||
</tt>". Now the object can be manipulated by commands of the
|
||||
form <tt>"Show[%, <parametername> ->
|
||||
<newvalue>]</tt>". </p>
|
||||
|
||||
<p>For Delaunay triangulation orthogonal projection is better.
|
||||
This can be specified, for example, by "<tt>BoxRatios:
|
||||
Show[%, BoxRatios -> {1, 1, 1e-8}]</tt>". To see the
|
||||
meaningful side of the 3-d object used to visualize 2-d Delaunay,
|
||||
you need to change the viewpoint: "<tt>Show[%, ViewPoint
|
||||
-> {0, 0, -1}]</tt>". By specifying different viewpoints
|
||||
you can slowly rotate objects. </p>
|
||||
|
||||
<p>For halfspace intersections, Qhull produces the dual
|
||||
convex hull.
|
||||
|
||||
<p>See <a href="qh-faq.htm#math">Is Qhull available for Mathematica?</a>
|
||||
for URLs.
|
||||
|
||||
<h3><a href="#output">»</a><a name="n">n - print hyperplane
|
||||
normals with offsets </a></h3>
|
||||
|
||||
<p>The first line is the dimension plus one. The second line is
|
||||
the number of facets. The remaining lines are the normals for
|
||||
each facet, one normal per line. The facet's offset follows its
|
||||
normal coefficients.</p>
|
||||
|
||||
<p>The normals point outward, i.e., the convex hull satisfies <i>Ax
|
||||
<= -b </i>where <i>A</i> is the matrix of coefficients and <i>b</i>
|
||||
is the vector of offsets.</p>
|
||||
|
||||
<p>A point is <i>inside</i> or <i>below</i> a hyperplane if its distance
|
||||
to the hyperplane is negative. A point is <i>outside</i> or <i>above</i> a hyperplane
|
||||
if its distance to the hyperplane is positive. Otherwise a point is <i>on</i> or
|
||||
<i>coplanar to</i> the hyperplane.
|
||||
|
||||
<p>If cdd output is specified ('<a href="qh-optf.htm#FD">FD</a>'),
|
||||
Qhull prints the command line, the keyword "begin", the
|
||||
number of facets, the dimension (plus one), the keyword
|
||||
"real", and the normals for each facet. The facet's
|
||||
negative offset precedes its normal coefficients (i.e., if the
|
||||
origin is an interior point, the offset is positive). Qhull ends
|
||||
the output with the keyword "end". </p>
|
||||
|
||||
<h3><a href="#output">»</a><a name="o">o - print OFF file format
|
||||
</a></h3>
|
||||
|
||||
<p>The output is: </p>
|
||||
|
||||
<ul>
|
||||
<li>The first line is the dimension </li>
|
||||
<li>The second line is the number of points, the number of
|
||||
facets, and the number of ridges. </li>
|
||||
<li>All of the input points follow, one per line. </li>
|
||||
<li>Then Qhull prints the vertices for each facet. Each facet
|
||||
is on a separate line. The first number is the number of
|
||||
vertices. The remainder is the indices of the
|
||||
corresponding points. The vertices are oriented in 2-d,
|
||||
3-d, and in simplicial facets. </li>
|
||||
</ul>
|
||||
|
||||
<p>Option '<a href="qh-optf.htm#Ft">Ft</a>' prints the same
|
||||
information with added points for non-simplicial facets.</p>
|
||||
|
||||
<p>Option '<a href="qh-opto.htm#i">i</a>' displays vertices
|
||||
without the point coordinates. Option '<a href="qh-opto.htm#p">p</a>'
|
||||
displays the point coordinates without vertex and facet information.</p>
|
||||
|
||||
<p>In 3-d, Geomview can load the file directly if you delete the
|
||||
first line (e.g., by piping through '<tt>tail +2</tt>').</p>
|
||||
|
||||
<p>For Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>), option
|
||||
'o' prints Voronoi vertices and Voronoi regions instead of input
|
||||
points and facets. The first vertex is the infinity vertex
|
||||
[-10.101, -10.101, ...]. Then, option 'o' lists the vertices in
|
||||
the Voronoi region for each input site. The regions appear in
|
||||
site ID order. In 2-d, the vertices of a Voronoi region are
|
||||
sorted by adjacency (non-oriented). In 3-d and higher, the
|
||||
Voronoi vertices are sorted by index. See the '<a
|
||||
href="qh-optf.htm#FN">FN</a>' option for listing Voronoi regions
|
||||
without listing Voronoi vertices.</p>
|
||||
|
||||
<p>If you are using the Qhull library, options 'v o' have the
|
||||
side effect of reordering the neighbors for a vertex.</p>
|
||||
|
||||
<h3><a href="#output">»</a><a name="p">p - print vertex and
|
||||
point coordinates </a></h3>
|
||||
|
||||
<p>The first line is the dimension. The second line is the number
|
||||
of vertices. The remaining lines are the vertices, one vertex per
|
||||
line. A vertex consists of its point coordinates</p>
|
||||
|
||||
<p>With the '<a href="qh-optg.htm#Gc">Gc</a>' and '<a
|
||||
href="qh-optg.htm#Gi">Gi</a>' options, option 'p' also prints
|
||||
coplanar and interior points respectively.</p>
|
||||
|
||||
<p>For <a href=qvoronoi.htm>qvoronoi</a>, it prints the
|
||||
coordinates of each Voronoi vertex.</p>
|
||||
|
||||
<p>For <a href=qdelaun.htm>qdelaunay</a>, it prints the
|
||||
input sites as lifted to a paraboloid. For <a href=qhalf.htm>qhalf</a>
|
||||
it prints the dual points. For both, option 'p' is the same as the first
|
||||
section of option '<a href="qh-opto.htm#o">o</a>'.</p>
|
||||
|
||||
<p>Use '<a href="qh-optf.htm#Fx">Fx</a>' to list the point ids of
|
||||
the extreme points (i.e., vertices). </p>
|
||||
|
||||
<p>If a subset of the facets is selected ('<a
|
||||
href="qh-optp.htm#Pdk">Pdk</a>', '<a href="qh-optp.htm#PDk">PDk</a>',
|
||||
'<a href="qh-optp.htm#Pg">Pg</a>' options), option 'p' only
|
||||
prints vertices and points associated with those facets.</p>
|
||||
|
||||
<p>If cdd-output format is selected ('<a href="qh-optf.htm#FD">FD</a>'),
|
||||
the first line is "begin". The second line is the
|
||||
number of vertices, the dimension plus one, and "real".
|
||||
The vertices follow with a leading "1". Output ends
|
||||
with "end". </p>
|
||||
|
||||
<h3><a href="#output">»</a><a name="s">s - print summary to
|
||||
stderr </a></h3>
|
||||
|
||||
<p>The default output of Qhull is a summary to stderr. Options '<a
|
||||
href="qh-optf.htm#FS">FS</a>' and '<a href="qh-optf.htm#Fs">Fs</a>'
|
||||
produce the same information for programs. <b>Note</b>: Windows 95 and 98
|
||||
treats stderr the same as stdout. Use option '<a href="qh-optt.htm#TO">TO file</a>' to separate
|
||||
stderr and stdout.</p>
|
||||
|
||||
<p>The summary lists the number of input points, the dimension,
|
||||
the number of vertices in the convex hull, and the number of
|
||||
facets in the convex hull. It lists the number of selected
|
||||
("good") facets for options '<a href="qh-optp.htm#Pg">Pg</a>',
|
||||
'<a href="qh-optp.htm#Pdk">Pdk</a>', <a href=qdelaun.htm>qdelaunay</a>,
|
||||
or <a href=qvoronoi.htm>qvoronoi</a> (Delaunay triangulations only
|
||||
use the lower half of a convex hull). It lists the number of
|
||||
coplanar points. For Delaunay triangulations without '<a
|
||||
href="qh-optq.htm#Qc">Qc</a>', it lists the total number of
|
||||
coplanar points. It lists the number of simplicial facets in
|
||||
the output.</p>
|
||||
|
||||
<p>The terminology depends on the output structure. </p>
|
||||
|
||||
<p>The summary lists these statistics:</p>
|
||||
|
||||
<ul>
|
||||
<li>number of points processed by Qhull </li>
|
||||
<li>number of hyperplanes created</li>
|
||||
<li>number of distance tests (not counting statistics,
|
||||
summary, and checking) </li>
|
||||
<li>number of merged facets (if any)</li>
|
||||
<li>number of distance tests for merging (if any)</li>
|
||||
<li>CPU seconds to compute the hull</li>
|
||||
<li>the maximum joggle for '<a href="qh-optq.htm#QJn">QJ</a>'<br>
|
||||
or, the probability of precision errors for '<a
|
||||
href="qh-optq.htm#QJn">QJ</a> <a href="qh-optt.htm#TRn">TRn</a>'
|
||||
</li>
|
||||
<li>total area and volume (if computed, see '<a
|
||||
href="qh-optf.htm#FS">FS</a>' '<a href="qh-optf.htm#FA">FA</a>'
|
||||
'<a href="qh-optf.htm#Fa">Fa</a>' '<a
|
||||
href="qh-optp.htm#PAn">PAn</a>')</li>
|
||||
<li>max. distance of a point above a facet (if non-zero)</li>
|
||||
<li>max. distance of a vertex below a facet (if non-zero)</li>
|
||||
</ul>
|
||||
|
||||
<p>The statistics include intermediate hulls. For example 'rbox d
|
||||
D4 | qhull' reports merged facets even though the final hull is
|
||||
simplicial. </p>
|
||||
|
||||
<p>Qhull starts counting CPU seconds after it has read and
|
||||
projected the input points. It stops counting before producing
|
||||
output. In the code, CPU seconds measures the execution time of
|
||||
function qhull() in <tt>libqhull.c</tt>. If the number of CPU
|
||||
seconds is clearly wrong, check qh_SECticks in <tt>user.h</tt>. </p>
|
||||
|
||||
<p>The last two figures measure the maximum distance from a point
|
||||
or vertex to a facet. They are not printed if less than roundoff
|
||||
or if not merging. They account for roundoff error in computing
|
||||
the distance (c.f., option '<a href="qh-optc.htm#Rn">Rn</a>').
|
||||
Use '<a href="qh-optf.htm#Fs">Fs</a>' to report the maximum outer
|
||||
and inner plane. </p>
|
||||
|
||||
<p>A number may appear in parentheses after the maximum distance
|
||||
(e.g., 2.1x). It is the ratio between the maximum distance and
|
||||
the worst-case distance due to merging two simplicial facets. It
|
||||
should be small for 2-d, 3-d, and 4-d, and for higher dimensions
|
||||
with '<a href="qh-optq.htm#Qx">Qx</a>'. It is not printed if less
|
||||
than 0.05. </p>
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<!-- 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>
|
||||
253
xs/src/qhull/html/qh-optp.htm
Normal file
@@ -0,0 +1,253 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull print options (P)</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<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> Qhull print options (P)</h1>
|
||||
|
||||
This section lists the print options for Qhull. These options are
|
||||
indicated by 'P' followed by a letter. See
|
||||
<a href="qh-opto.htm#output">Output</a>, <a href="qh-optg.htm#geomview">Geomview</a>,
|
||||
and <a href="qh-optf.htm#format">Format</a> for other output options.
|
||||
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="format">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Print options</h2>
|
||||
<blockquote>
|
||||
<dl compact>
|
||||
<dt> </dt>
|
||||
<dd><b>General</b></dd>
|
||||
<dt><a href="#Pp">Pp</a></dt>
|
||||
<dd>do not report precision problems </dd>
|
||||
<dt><a href="#Po">Po</a></dt>
|
||||
<dd>force output despite precision problems</dd>
|
||||
<dt><a href="#Po2">Po</a></dt>
|
||||
<dd>if error, output neighborhood of facet</dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Select</b></dd>
|
||||
<dt><a href="#Pdk">Pdk:n</a></dt>
|
||||
<dd>print facets with normal[k] >= n (default 0.0)</dd>
|
||||
<dt><a href="#PDk">PDk:n</a></dt>
|
||||
<dd>print facets with normal[k] <= n </dd>
|
||||
<dt><a href="#PFn">PFn</a></dt>
|
||||
<dd>print facets whose area is at least n</dd>
|
||||
<dt><a href="#Pg">Pg</a></dt>
|
||||
<dd>print good facets only (needs '<a href="qh-optq.htm#QGn">QGn</a>'
|
||||
or '<a href="qh-optq.htm#QVn">QVn </a>')</dd>
|
||||
<dt><a href="#PMn">PMn</a></dt>
|
||||
<dd>print n facets with most merges</dd>
|
||||
<dt><a href="#PAn">PAn</a></dt>
|
||||
<dd>print n largest facets by area</dd>
|
||||
<dt><a href="#PG">PG</a></dt>
|
||||
<dd>print neighbors of good facets</dd>
|
||||
</dl>
|
||||
</blockquote>
|
||||
<hr>
|
||||
|
||||
<h3><a href="#print">»</a><a name="PAn">PAn - keep n largest
|
||||
facets by area</a></h3>
|
||||
|
||||
<p>The <i>n</i> largest facets are marked good for printing. This
|
||||
may be useful for <a href="qh-impre.htm#approximate">approximating
|
||||
a hull</a>. Unless '<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>'
|
||||
is automatically set. </p>
|
||||
|
||||
<h3><a href="#print">»</a><a name="Pdk">Pdk:n - print facet if
|
||||
normal[k] >= n </a></h3>
|
||||
|
||||
<p>For a given output, print only those facets with <i>normal[k] >= n</i>
|
||||
and <i>drop</i> the others. For example, 'Pd0:0.5' prints facets with <i>normal[0]
|
||||
>= 0.5 </i>. The default value of <i>n</i> is zero. For
|
||||
example in 3-d, 'Pd0d1d2' prints facets in the positive octant.
|
||||
<p>
|
||||
If no facets match, the closest facet is returned.</p>
|
||||
<p>
|
||||
On Windows 95, do not combine multiple options. A 'd' is considered
|
||||
part of a number. For example, use 'Pd0:0.5 Pd1:0.5' instead of
|
||||
'Pd0:0.5d1:0.5'.
|
||||
|
||||
<h3><a href="#print">»</a><a name="PDk">PDk:n - print facet if
|
||||
normal[k] <= n</a></h3>
|
||||
|
||||
<p>For a given output, print only those facets with <i>normal[k] <= n</i>
|
||||
and <i>drop</i> the others.
|
||||
For example, 'PD0:0.5' prints facets with <i>normal[0]
|
||||
<= 0.5 </i>. The default value of <i>n</i> is zero. For
|
||||
example in 3-d, 'PD0D1D2' displays facets in the negative octant.
|
||||
<p>
|
||||
If no facets match, the closest facet is returned.</p>
|
||||
|
||||
<p>In 2-d, 'd G PD2' displays the Delaunay triangulation instead
|
||||
of the corresponding paraboloid. </p>
|
||||
|
||||
<p>Be careful of placing 'Dk' or 'dk' immediately after a real
|
||||
number. Some compilers treat the 'D' as a double precision
|
||||
exponent. </p>
|
||||
|
||||
<h3><a href="#print">»</a><a name="PFn">PFn - keep facets whose
|
||||
area is at least n</a></h3>
|
||||
|
||||
<p>The facets with area at least <i>n</i> are marked good for
|
||||
printing. This may be useful for <a
|
||||
href="qh-impre.htm#approximate">approximating a hull</a>. Unless
|
||||
'<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>' is
|
||||
automatically set. </p>
|
||||
|
||||
<h3><a href="#print">»</a><a name="Pg">Pg - print good facets </a></h3>
|
||||
|
||||
<p>Qhull can mark facets as "good". This is used to</p>
|
||||
|
||||
<ul>
|
||||
<li>mark the lower convex hull for Delaunay triangulations
|
||||
and Voronoi diagrams</li>
|
||||
<li>mark the facets that are visible from a point (the '<a
|
||||
href="qh-optq.htm#QGn">QGn </a>' option)</li>
|
||||
<li>mark the facets that contain a point (the '<a
|
||||
href="qh-optq.htm#QVn">QVn</a>' option).</li>
|
||||
<li>indicate facets with a large enough area (options '<a
|
||||
href="#PAn">PAn</a>' and '<a href="#PFn">PFn</a>')</li>
|
||||
</ul>
|
||||
|
||||
<p>Option '<a href="#Pg">Pg</a>' only prints good facets that
|
||||
also meet '<a href="#Pdk">Pdk</a>' and '<a href="#PDk">PDk</a>'
|
||||
options. It is automatically set for options '<a href="#PAn">PAn</a>',
|
||||
'<a href="#PFn">PFn </a>', '<a href="qh-optq.htm#QGn">QGn</a>',
|
||||
and '<a href="qh-optq.htm#QVn">QVn</a>'.</p>
|
||||
|
||||
<h3><a href="#print">»</a><a name="PG">PG - print neighbors of
|
||||
good facets</a></h3>
|
||||
|
||||
<p>Option 'PG' can be used with or without option '<a href="#Pg">Pg</a>'
|
||||
to print the neighbors of good facets. For example, options '<a
|
||||
href="qh-optq.htm#QGn">QGn</a>' and '<a href="qh-optq.htm#QVn">QVn</a>'
|
||||
print the horizon facets for point <i>n. </i></p>
|
||||
|
||||
<h3><a href="#print">»</a><a name="PMn">PMn - keep n facets with
|
||||
most merges</a></h3>
|
||||
|
||||
<p>The n facets with the most merges are marked good for
|
||||
printing. This may be useful for <a
|
||||
href="qh-impre.htm#approximate">approximating a hull</a>. Unless
|
||||
'<a href="#PG">PG</a>' is set, '<a href="#Pg">Pg</a>' is
|
||||
automatically set. </p>
|
||||
|
||||
<p>Use option '<a href="qh-optf.htm#Fm">Fm</a>' to print merges
|
||||
per facet.
|
||||
|
||||
<h3><a href="#print">»</a><a name="Po">Po - force output despite
|
||||
precision problems</a></h3>
|
||||
|
||||
<p>Use options 'Po' and '<a href="qh-optq.htm#Q0">Q0</a>' if you
|
||||
can not merge facets, triangulate the output ('<a href="qh-optq.htm#Qt">Qt</a>'),
|
||||
or joggle the input (<a href="qh-optq.htm#QJn">QJ</a>).
|
||||
|
||||
<p>Option 'Po' can not force output when
|
||||
duplicate ridges or duplicate facets occur. It may produce
|
||||
erroneous results. For these reasons, merged facets, joggled input, or <a
|
||||
href="qh-impre.htm#exact">exact arithmetic</a> are better.</p>
|
||||
|
||||
<p>If you need a simplicial Delaunay triangulation, use
|
||||
joggled input '<a href="qh-optq.htm#QJn">QJ</a>' or triangulated
|
||||
output '<a
|
||||
href="qh-optf.htm#Ft">Ft</a>'.
|
||||
|
||||
<p>Option 'Po' may be used without '<a href="qh-optq.htm#Q0">Q0</a>'
|
||||
to remove some steps from Qhull or to output the neighborhood of
|
||||
an error.</p>
|
||||
|
||||
<p>Option 'Po' may be used with option '<a href="qh-optq.htm#Q5">Q5</a>')
|
||||
to skip qh_check_maxout (i.e., do not determine the maximum outside distance).
|
||||
This can save a significant amount of time.
|
||||
|
||||
<p>If option 'Po' is used,</p>
|
||||
|
||||
<ul>
|
||||
<li>most precision errors allow Qhull to continue. </li>
|
||||
<li>verify ('<a href="qh-optt.htm#Tv">Tv</a>') does not check
|
||||
coplanar points.</li>
|
||||
<li>points are not partitioned into flipped facets and a
|
||||
flipped facet is always visible to a point. This may
|
||||
delete flipped facets from the output. </li>
|
||||
</ul>
|
||||
|
||||
<h3><a href="#print">»</a><a name="Po2">Po - if error, output
|
||||
neighborhood of facet</a></h3>
|
||||
|
||||
<p>If an error occurs before the completion of Qhull and tracing
|
||||
is not active, 'Po' outputs a neighborhood of the erroneous
|
||||
facets (if any). It uses the current output options.</p>
|
||||
|
||||
<p>See '<a href="qh-optp.htm#Po">Po</a>' - force output despite
|
||||
precision problems.
|
||||
|
||||
<h3><a href="#print">»</a><a name="Pp">Pp - do not report
|
||||
precision problems </a></h3>
|
||||
|
||||
<p>With option 'Pp', Qhull does not print statistics about
|
||||
precision problems, and it removes some of the warnings. It
|
||||
removes the narrow hull warning.</p>
|
||||
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<!-- 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>
|
||||
731
xs/src/qhull/html/qh-optq.htm
Normal file
@@ -0,0 +1,731 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Qhull control options (Q)</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navigation links -->
|
||||
<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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<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> Qhull control options (Q)</h1>
|
||||
|
||||
<p>This section lists the control options for Qhull. These
|
||||
options are indicated by 'Q' followed by a letter. </p>
|
||||
|
||||
<p><b>Copyright © 1995-2015 C.B. Barber</b></p>
|
||||
|
||||
<hr>
|
||||
|
||||
<p><a href="index.htm#TOC">»</a> <a href="qh-quick.htm#programs">Programs</a>
|
||||
<a name="qhull">•</a> <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
|
||||
<h2>Qhull control options</h2>
|
||||
|
||||
<dl compact>
|
||||
<dt> </dt>
|
||||
<dd><b>General</b></dd>
|
||||
<dt><a href="#Qu">Qu</a></dt>
|
||||
<dd>compute upper hull for furthest-site Delaunay
|
||||
triangulation </dd>
|
||||
<dt><a href="#Qc">Qc</a></dt>
|
||||
<dd>keep coplanar points with nearest facet</dd>
|
||||
<dt><a href="#Qi">Qi</a></dt>
|
||||
<dd>keep interior points with nearest facet</dd>
|
||||
<dt><a href="#QJn">QJ</a></dt>
|
||||
<dd>joggled input to avoid precision problems</dd>
|
||||
<dt><a href="#Qt">Qt</a></dt>
|
||||
<dd>triangulated output</dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Precision handling</b></dd>
|
||||
<dt><a href="#Qz">Qz</a></dt>
|
||||
<dd>add a point-at-infinity for Delaunay triangulations</dd>
|
||||
<dt><a href="#Qx">Qx</a></dt>
|
||||
<dd>exact pre-merges (allows coplanar facets)</dd>
|
||||
<dt><a href="#Qs">Qs</a></dt>
|
||||
<dd>search all points for the initial simplex</dd>
|
||||
<dt><a href="#Qbb">Qbb</a></dt>
|
||||
<dd>scale last coordinate to [0,m] for Delaunay</dd>
|
||||
<dt><a href="#Qv">Qv</a></dt>
|
||||
<dd>test vertex neighbors for convexity</dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Transform input</b></dd>
|
||||
<dt><a href="#Qb0">Qbk:0Bk:0</a></dt>
|
||||
<dd>drop dimension k from input</dd>
|
||||
<dt><a href="#QRn">QRn</a></dt>
|
||||
<dd>random rotation (n=seed, n=0 time, n=-1 time/no rotate)</dd>
|
||||
<dt><a href="#Qbk">Qbk:n</a></dt>
|
||||
<dd>scale coord[k] to low bound of n (default -0.5)</dd>
|
||||
<dt><a href="#QBk">QBk:n</a></dt>
|
||||
<dd>scale coord[k] to upper bound of n (default 0.5)</dd>
|
||||
<dt><a href="#QbB">QbB</a></dt>
|
||||
<dd>scale input to fit the unit cube</dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Select facets</b></dd>
|
||||
<dt><a href="#QVn">QVn</a></dt>
|
||||
<dd>good facet if it includes point n, -n if not</dd>
|
||||
<dt><a href="#QGn">QGn</a></dt>
|
||||
<dd>good facet if visible from point n, -n for not visible</dd>
|
||||
<dt><a href="#Qg">Qg</a></dt>
|
||||
<dd>only build good facets (needs '<a href="#QGn">QGn</a>', '<a
|
||||
href="#QVn">QVn </a>', or '<a href="qh-optp.htm#Pdk">Pdk</a>')</dd>
|
||||
<dt> </dt>
|
||||
<dt> </dt>
|
||||
<dd><b>Experimental</b></dd>
|
||||
<dt><a href="#Q4">Q4</a></dt>
|
||||
<dd>avoid merging old facets into new facets</dd>
|
||||
<dt><a href="#Q5">Q5</a></dt>
|
||||
<dd>do not correct outer planes at end of qhull</dd>
|
||||
<dt><a href="#Q3">Q3</a></dt>
|
||||
<dd>do not merge redundant vertices</dd>
|
||||
<dt><a href="#Q6">Q6</a></dt>
|
||||
<dd>do not pre-merge concave or coplanar facets</dd>
|
||||
<dt><a href="#Q0">Q0</a></dt>
|
||||
<dd>do not pre-merge facets with 'C-0' or 'Qx'</dd>
|
||||
<dt><a href="#Q8">Q8</a></dt>
|
||||
<dd>ignore near-interior points</dd>
|
||||
<dt><a href="#Q2">Q2</a></dt>
|
||||
<dd>merge all non-convex at once instead of independent sets</dd>
|
||||
<dt><a href="#Qf">Qf</a></dt>
|
||||
<dd>partition point to furthest outside facet</dd>
|
||||
<dt><a href="#Q7">Q7</a></dt>
|
||||
<dd>process facets depth-first instead of breadth-first</dd>
|
||||
<dt><a href="#Q9">Q9</a></dt>
|
||||
<dd>process furthest of furthest points</dd>
|
||||
<dt><a href="#Q10">Q10</a></dt>
|
||||
<dd>no special processing for narrow distributions</dd>
|
||||
<dt><a href="#Q11">Q11</a></dt>
|
||||
<dd>copy normals and recompute centrums for tricoplanar facets</dd>
|
||||
<dt><a href="#Q12">Q12</a></dt>
|
||||
<dd>do not error on wide merge due to duplicate ridge and nearly coincident points</dd>
|
||||
<dt><a href="#Qm">Qm</a></dt>
|
||||
<dd>process points only if they would increase the max. outer
|
||||
plane </dd>
|
||||
<dt><a href="#Qr">Qr</a></dt>
|
||||
<dd>process random outside points instead of furthest one</dd>
|
||||
<dt><a href="#Q1">Q1</a></dt>
|
||||
<dd>sort merges by type instead of angle</dd>
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qbb">Qbb - scale the last
|
||||
coordinate to [0,m] for Delaunay</a></h3>
|
||||
|
||||
<p>After scaling with option 'Qbb', the lower bound of the last
|
||||
coordinate will be 0 and the upper bound will be the maximum
|
||||
width of the other coordinates. Scaling happens after projecting
|
||||
the points to a paraboloid and scaling other coordinates. </p>
|
||||
|
||||
<p>Option 'Qbb' is automatically set for <a href=qdelaun.htm>qdelaunay</a>
|
||||
and <a href=qvoronoi.htm>qvoronoi</a>. Option 'Qbb' is automatically set for joggled input '<a
|
||||
href="qh-optq.htm#QJn">QJ</a>'. </p>
|
||||
|
||||
<p>Option 'Qbb' should be used for Delaunay triangulations with
|
||||
integer coordinates. Since the last coordinate is the sum of
|
||||
squares, it may be much larger than the other coordinates. For
|
||||
example, <tt>rbox 10000 D2 B1e8 | qhull d</tt> has precision
|
||||
problems while <tt>rbox 10000 D2 B1e8 | qhull d Qbb</tt> is OK. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QbB">QbB - scale the input to
|
||||
fit the unit cube</a></h3>
|
||||
|
||||
<p>After scaling with option 'QbB', the lower bound will be -0.5
|
||||
and the upper bound +0.5 in all dimensions. For different bounds
|
||||
change qh_DEFAULTbox in <tt>user.h</tt> (0.5 is best for <a
|
||||
href="index.htm#geomview">Geomview</a>).</p>
|
||||
|
||||
<p>For Delaunay and Voronoi diagrams, scaling happens after
|
||||
projection to the paraboloid. Under precise arithmetic, scaling
|
||||
does not change the topology of the convex hull. Scaling may
|
||||
reduce precision errors if coordinate values vary widely.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qbk">Qbk:n - scale coord[k]
|
||||
to low bound</a></h3>
|
||||
|
||||
<p>After scaling, the lower bound for dimension k of the input
|
||||
points will be n. 'Qbk' scales coord[k] to -0.5. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QBk">QBk:n - scale coord[k]
|
||||
to upper bound </a></h3>
|
||||
|
||||
<p>After scaling, the upper bound for dimension k of the input
|
||||
points will be n. 'QBk' scales coord[k] to 0.5. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qb0">Qbk:0Bk:0 - drop
|
||||
dimension k from the input points</a></h3>
|
||||
|
||||
<p>Drop dimension<em> k </em>from the input points. For example,
|
||||
'Qb1:0B1:0' deletes the y-coordinate from all input points. This
|
||||
allows the user to take convex hulls of sub-dimensional objects.
|
||||
It happens before the Delaunay and Voronoi transformation.
|
||||
It happens after the halfspace transformation for both the data
|
||||
and the feasible point.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qc">Qc - keep coplanar points
|
||||
with nearest facet </a></h3>
|
||||
|
||||
<p>During construction of the hull, a point is coplanar if it is
|
||||
between '<a href="qh-optc.htm#Wn">Wn</a>' above and '<a
|
||||
href="qh-optc.htm#Un">Un</a>' below a facet's hyperplane. A
|
||||
different definition is used for output from Qhull. </p>
|
||||
|
||||
<p>For output, a coplanar point is above the minimum vertex
|
||||
(i.e., above the inner plane). With joggle ('<a
|
||||
href="qh-optq.htm#QJn">QJ</a>'), a coplanar point includes points
|
||||
within one joggle of the inner plane. </p>
|
||||
|
||||
<p>With option 'Qc', output formats '<a href="qh-opto.htm#p">p </a>',
|
||||
'<a href="qh-opto.htm#f">f</a>', '<a href="qh-optg.htm#Gp">Gp</a>',
|
||||
'<a href="qh-optf.htm#Fc">Fc</a>', '<a href="qh-optf.htm#FN">FN</a>',
|
||||
and '<a href="qh-optf.htm#FP">FP</a>' will print the coplanar
|
||||
points. With options 'Qc <a href="#Qi">Qi</a>' these outputs
|
||||
include the interior points.</p>
|
||||
|
||||
<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>
|
||||
or <a href=qvoronoi.htm>qvoronoi</a>), a coplanar point is a point
|
||||
that is nearly incident to a vertex. All input points are either
|
||||
vertices of the triangulation or coplanar.</p>
|
||||
|
||||
<p>Qhull stores coplanar points with a facet. While constructing
|
||||
the hull, it retains all points within qh_RATIOnearInside
|
||||
(user.h) of a facet. In qh_check_maxout(), it uses these points
|
||||
to determine the outer plane for each facet. With option 'Qc',
|
||||
qh_check_maxout() retains points above the minimum vertex for the
|
||||
hull. Other points are removed. If qh_RATIOnearInside is wrong or
|
||||
if options '<a href="#Q5">Q5</a> <a href="#Q8">Q8</a>' are set, a
|
||||
coplanar point may be missed in the output (see <a
|
||||
href="qh-impre.htm#limit">Qhull limitations</a>).</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qf">Qf - partition point to
|
||||
furthest outside facet </a></h3>
|
||||
|
||||
<p>After adding a new point to the convex hull, Qhull partitions
|
||||
the outside points and coplanar points of the old, visible
|
||||
facets. Without the '<a href="qh-opto.htm#f">f </a>' option and
|
||||
merging, it assigns a point to the first facet that it is outside
|
||||
('<a href="qh-optc.htm#Wn">Wn</a>'). When merging, it assigns a
|
||||
point to the first facet that is more than several times outside
|
||||
(see qh_DISToutside in user.h).</p>
|
||||
|
||||
<p>If option 'Qf' is selected, Qhull performs a directed search
|
||||
(no merging) or an exhaustive search (merging) of new facets.
|
||||
Option 'Qf' may reduce precision errors if pre-merging does not
|
||||
occur.</p>
|
||||
|
||||
<p>Option '<a href="#Q9">Q9</a>' processes the furthest of all
|
||||
furthest points.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qg">Qg - only build good
|
||||
facets (needs 'QGn' 'QVn' or 'Pdk') </a></h3>
|
||||
|
||||
<p>Qhull has several options for defining and printing good
|
||||
facets. With the '<a href="#Qg">Qg</a>' option, Qhull will only
|
||||
build those facets that it needs to determine the good facets in
|
||||
the output. This may speed up Qhull in 2-d and 3-d. It is
|
||||
useful for furthest-site Delaunay
|
||||
triangulations (<a href=qdelau_f.htm>qdelaunay Qu</a>,
|
||||
invoke with 'qhull d Qbb <a href="#Qu">Qu</a> Qg').
|
||||
It is not effective in higher
|
||||
dimensions because many facets see a given point and contain a
|
||||
given vertex. It is not guaranteed to work for all combinations.</p>
|
||||
|
||||
<p>See '<a href="#QGn">QGn</a>', '<a href="#QVn">QVn</a>', and '<a
|
||||
href="qh-optp.htm#Pdk">Pdk</a>' for defining good facets, and '<a
|
||||
href="qh-optp.htm#Pg">Pg</a>' and '<a href="qh-optp.htm#PG">PG</a>'
|
||||
for printing good facets and their neighbors. If pre-merging ('<a
|
||||
href="qh-optc.htm#Cn">C-n</a>') is not used and there are
|
||||
coplanar facets, then 'Qg Pg' may produce a different result than
|
||||
'<a href="qh-optp.htm#Pg">Pg</a>'. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QGn">QGn - good facet if
|
||||
visible from point n, -n for not visible </a></h3>
|
||||
|
||||
<p>With option 'QGn', a facet is good (see '<a href="#Qg">Qg</a>'
|
||||
and '<a href="qh-optp.htm#Pg">Pg</a>') if it is visible from
|
||||
point n. If <i>n < 0</i>, a facet is good if it is not visible
|
||||
from point n. Point n is not added to the hull (unless '<a
|
||||
href="qh-optt.htm#TCn">TCn</a>' or '<a href="qh-optt.htm#TPn">TPn</a>').</p>
|
||||
|
||||
<p>With <a href="rbox.htm">rbox</a>, use the 'Pn,m,r' option
|
||||
to define your point; it will be point 0 ('QG0'). </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qi">Qi - keep interior points
|
||||
with nearest facet </a></h3>
|
||||
|
||||
<p>Normally Qhull ignores points that are clearly interior to the
|
||||
convex hull. With option 'Qi', Qhull treats interior points the
|
||||
same as coplanar points. Option 'Qi' does not retain coplanar
|
||||
points. You will probably want '<a href="#Qc">Qc </a>' as well. </p>
|
||||
|
||||
<p>Option 'Qi' is automatically set for '<a href=qdelaun.htm>qdelaunay</a>
|
||||
<a href="#Qc">Qc</a>' and '<a href=qvoronoi.htm>qvoronoi</a>
|
||||
<a href="#Qc">Qc</a>'. If you use
|
||||
'<a href=qdelaun.htm>qdelaunay</a> Qi' or '<a href=qvoronoi.htm>qvoronoi</a>
|
||||
Qi', option '<a href="qh-opto.htm#s">s</a>' reports all nearly
|
||||
incident points while option '<a href="qh-optf.htm#Fs">Fs</a>'
|
||||
reports the number of interior points (should always be zero).</p>
|
||||
|
||||
<p>With option 'Qi', output formats '<a href="qh-opto.htm#p">p</a>',
|
||||
'<a href="qh-opto.htm#f">f</a>','<a href="qh-optg.htm#Gp">Gp</a>',
|
||||
'<a href="qh-optf.htm#Fc">Fc</a>', '<a href="qh-optf.htm#FN">FN</a>',
|
||||
and '<a href="qh-optf.htm#FP">FP</a>' include interior points. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QJ">QJ</a> or <a name="QJn">QJn</a> - joggled
|
||||
input to avoid precision errors</a></h3>
|
||||
|
||||
<p>Option 'QJ' or 'QJn' joggles each input coordinate by adding a
|
||||
random number in the range [-n,n]. If a precision error occurs,
|
||||
It tries again. If precision errors still occur, Qhull increases <i>n</i>
|
||||
ten-fold and tries again. The maximum value for increasing <i>n</i>
|
||||
is 0.01 times the maximum width of the input. Option 'QJ' selects
|
||||
a default value for <i>n</i>. <a href="../src/user.h#JOGGLEdefault">User.h</a>
|
||||
defines these parameters and a maximum number of retries. See <a
|
||||
href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>
|
||||
|
||||
<p>Users of joggled input should consider converting to
|
||||
triangulated output ('<a href="../html/qh-optq.htm#Qt">Qt</a>'). Triangulated output is
|
||||
approximately 1000 times more accurate than joggled input.
|
||||
|
||||
<p>Option 'QJ' also sets '<a href="qh-optq.htm#Qbb">Qbb</a>' for
|
||||
Delaunay triangulations and Voronoi diagrams. It does not set
|
||||
'Qbb' if '<a href="qh-optq.htm#Qbk">Qbk:n</a>' or '<a
|
||||
href="qh-optq.htm#QBk">QBk:n</a>' are set. </p>
|
||||
|
||||
<p>If 'QJn' is set, Qhull does not merge facets unless requested
|
||||
to. All facets are simplicial (triangular in 2-d). This may be
|
||||
important for your application. You may also use triangulated output
|
||||
('<a href="qh-optq.htm#Qt">Qt</a>') or Option '<a href="qh-optf.htm#Ft">Ft</a>'.
|
||||
|
||||
<p>Qhull adjusts the outer and inner planes for 'QJn' ('<a
|
||||
href="qh-optf.htm#Fs">Fs</a>'). They are increased by <i>sqrt(d)*n</i>
|
||||
to account for the maximum distance between a joggled point and
|
||||
the corresponding input point. Coplanar points ('<a
|
||||
href="qh-optq.htm#Qc">Qc</a>') require an additional <i>sqrt(d)*n</i>
|
||||
since vertices and coplanar points may be joggled in opposite
|
||||
directions. </p>
|
||||
|
||||
<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>), joggle
|
||||
happens before lifting the input sites to a paraboloid. Instead of
|
||||
'QJ', you may use triangulated output ('<a
|
||||
href="qh-optq.htm#Qt">Qt</a>')</p>
|
||||
|
||||
<p>This option is deprecated for Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>).
|
||||
It triangulates cospherical points, leading to duplicated Voronoi vertices.</p>
|
||||
|
||||
<p>By default, 'QJn' uses a fixed random number seed. To use time
|
||||
as the random number seed, select '<a href="qh-optq.htm#QRn">QR-1</a>'.
|
||||
The summary ('<a href="qh-opto.htm#s">s</a>') will show the
|
||||
selected seed as 'QR-n'.
|
||||
|
||||
<p>With 'QJn', Qhull does not error on degenerate hyperplane
|
||||
computations. Except for Delaunay and Voronoi computations, Qhull
|
||||
does not error on coplanar points. </p>
|
||||
|
||||
<p>Use option '<a href="qh-optf.htm#FO">FO</a>' to display the
|
||||
selected options. Option 'FO' displays the joggle and the joggle
|
||||
seed. If Qhull restarts, it will redisplay the options. </p>
|
||||
|
||||
<p>Use option '<a href="qh-optt.htm#TRn">TRn</a>' to estimate the
|
||||
probability that Qhull will fail for a given 'QJn'.
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qm">Qm - only process points
|
||||
that increase the maximum outer plane </a></h3>
|
||||
|
||||
<p>Qhull reports the maximum outer plane in its summary ('<a
|
||||
href="qh-opto.htm#s">s</a>'). With option 'Qm', Qhull does not
|
||||
process points that are below the current, maximum outer plane.
|
||||
This is equivalent to always adjusting '<a href="qh-optc.htm#Wn">Wn
|
||||
</a>' to the maximum distance of a coplanar point to a facet. It
|
||||
is ignored for points above the upper convex hull of a Delaunay
|
||||
triangulation. Option 'Qm' is no longer important for merging.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qr">Qr - process random
|
||||
outside points instead of furthest ones </a></h3>
|
||||
|
||||
<p>Normally, Qhull processes the furthest point of a facet's
|
||||
outside points. Option 'Qr' instead selects a random outside
|
||||
point for processing. This makes Qhull equivalent to the
|
||||
randomized incremental algorithms.</p>
|
||||
|
||||
<p>The original randomization algorithm by Clarkson and Shor [<a
|
||||
href="index.htm#cla-sho89">'89</a>] used a conflict list which
|
||||
is equivalent to Qhull's outside set. Later randomized algorithms
|
||||
retained the previously constructed facets. </p>
|
||||
|
||||
<p>To compare Qhull to the randomized algorithms with option
|
||||
'Qr', compare "hyperplanes constructed" and
|
||||
"distance tests". Qhull does not report CPU time
|
||||
because the randomization is inefficient. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QRn">QRn - random rotation </a></h3>
|
||||
|
||||
<p>Option 'QRn' randomly rotates the input. For Delaunay
|
||||
triangulations (<a href=qdelaun.htm>qdelaunay</a> or <a href=qvoronoi.htm>qvoronoi</a>),
|
||||
it rotates the lifted points about
|
||||
the last axis. </p>
|
||||
|
||||
<p>If <em>n=0</em>, use time as the random number seed. If <em>n>0</em>,
|
||||
use n as the random number seed. If <em>n=-1</em>, don't rotate
|
||||
but use time as the random number seed. If <em>n<-1</em>,
|
||||
don't rotate but use <em>n</em> as the random number seed. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qs">Qs - search all points
|
||||
for the initial simplex </a></h3>
|
||||
|
||||
<p>Qhull constructs an initial simplex from <i>d+1</i> points. It
|
||||
selects points with the maximum and minimum coordinates and
|
||||
non-zero determinants. If this fails, it searches all other
|
||||
points. In 8-d and higher, Qhull selects points with the minimum
|
||||
x or maximum coordinate (see qh_initialvertices in <tt>poly2.c </tt>).
|
||||
It rejects points with nearly zero determinants. This should work
|
||||
for almost all input sets.</p>
|
||||
|
||||
<p>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="#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. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qt">Qt - triangulated output</a></h3>
|
||||
|
||||
<p>By default, qhull merges facets to handle precision errors. This
|
||||
produces non-simplicial facets (e.g., the convex hull of a cube has 6 square
|
||||
facets). Each facet is non-simplicial because it has four vertices.
|
||||
|
||||
<p>Use option 'Qt' to triangulate all non-simplicial facets before generating
|
||||
results. Alternatively, use joggled input ('<a href="#QJn">QJ</a>') to
|
||||
prevent non-simplical facets. Unless '<a href="qh-optp.htm#Pp">Pp</a>' is set,
|
||||
qhull produces a warning if 'QJ' and 'Qt' are used together.
|
||||
|
||||
<p>For Delaunay triangulations (<a href=qdelaun.htm>qdelaunay</a>), triangulation
|
||||
occurs after lifting the input sites to a paraboloid and computing the convex hull.
|
||||
</p>
|
||||
|
||||
<p>Option 'Qt' is deprecated for Voronoi diagrams (<a href=qvoronoi.htm>qvoronoi</a>).
|
||||
It triangulates cospherical points, leading to duplicated Voronoi vertices.</p>
|
||||
|
||||
<p>Option 'Qt' may produce degenerate facets with zero area.</p>
|
||||
|
||||
<p>Facet area and hull volumes may differ with and without
|
||||
'Qt'. The triangulations are different and different triangles
|
||||
may be ignored due to precision errors.
|
||||
|
||||
<p>With sufficient merging, the ridges of a non-simplicial facet may share more than two neighboring facets. If so, their triangulation ('<a href="#Qt">Qt</a>') will fail since two facets have the same vertex set. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qu">Qu - compute upper hull
|
||||
for furthest-site Delaunay triangulation </a></h3>
|
||||
|
||||
<p>When computing a Delaunay triangulation (<a href=qdelaun.htm>qdelaunay</a>
|
||||
or <a href=qvoronoi.htm>qvoronoi</a>),
|
||||
Qhull computes both the the convex hull of points on a
|
||||
paraboloid. It normally prints facets of the lower hull. These
|
||||
correspond to the Delaunay triangulation. With option 'Qu', Qhull
|
||||
prints facets of the upper hull. These correspond to the <a
|
||||
href="qdelau_f.htm">furthest-site Delaunay triangulation</a>
|
||||
and the <a href="qvoron_f.htm">furthest-site Voronoi diagram</a>.</p>
|
||||
|
||||
<p>Option 'qhull d Qbb Qu <a href="#Qg">Qg</a>' may improve the speed of option
|
||||
'Qu'. If you use the Qhull library, a faster method is 1) use
|
||||
Qhull to compute the convex hull of the input sites; 2) take the
|
||||
extreme points (vertices) of the convex hull; 3) add one interior
|
||||
point (e.g.,
|
||||
'<a href="qh-optf.htm#FV">FV</a>', the average of <em>d</em> extreme points); 4) run
|
||||
'qhull d Qbb Qu' or 'qhull v Qbb Qu' on these points.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qv">Qv - test vertex
|
||||
neighbors for convexity </a></h3>
|
||||
|
||||
<p>Normally, Qhull tests all facet neighbors for convexity.
|
||||
Non-neighboring facets which share a vertex may not satisfy the
|
||||
convexity constraint. This occurs when a facet undercuts the
|
||||
centrum of another facet. They should still be convex. Option
|
||||
'Qv' extends Qhull's convexity testing to all neighboring facets
|
||||
of each vertex. The extra testing occurs after the hull is
|
||||
constructed.. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="QVn">QVn - good facet if it
|
||||
includes point n, -n if not </a></h3>
|
||||
|
||||
<p>With option 'QVn', a facet is good ('<a href="#Qg">Qg</a>',
|
||||
'<a href="qh-optp.htm#Pg">Pg</a>') if one of its vertices is
|
||||
point n. If <i>n<0</i>, a good facet does not include point n.
|
||||
|
||||
<p>If options '<a href="qh-optp.htm#PG">PG</a>'
|
||||
and '<a href="#Qg">Qg</a>' are not set, option '<a href="qh-optp.htm#Pg">Pg</a>'
|
||||
(print only good)
|
||||
is automatically set.
|
||||
</p>
|
||||
|
||||
<p>Option 'QVn' behaves oddly with options '<a href="qh-optf.htm#Fx">Fx</a>'
|
||||
and '<a href=qvoronoi.htm>qvoronoi</a> <a href="qh-optf.htm#Fv2">Fv</a>'.
|
||||
|
||||
<p>If used with option '<a href="#Qg">Qg</a>' (only process good facets), point n is
|
||||
either in the initial simplex or it is the first
|
||||
point added to the hull. Options 'QVn Qg' require either '<a href="#QJn">QJ</a>' or
|
||||
'<a href="#Q0">Q0</a>' (no merging).</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qx">Qx - exact pre-merges
|
||||
(allows coplanar facets) </a></h3>
|
||||
|
||||
<p>Option 'Qx' performs exact merges while building the hull.
|
||||
Option 'Qx' is set by default in 5-d and higher. Use option '<a
|
||||
href="#Q0">Q0</a>' to not use 'Qx' by default. Unless otherwise
|
||||
specified, option 'Qx' sets option '<a href="qh-optc.htm#C0">C-0</a>'.
|
||||
</p>
|
||||
|
||||
<p>The "exact" merges are merging a point into a
|
||||
coplanar facet (defined by '<a href="qh-optc.htm#Vn">Vn </a>', '<a
|
||||
href="qh-optc.htm#Un">Un</a>', and '<a href="qh-optc.htm#Cn">C-n</a>'),
|
||||
merging concave facets, merging duplicate ridges, and merging
|
||||
flipped facets. Coplanar merges and angle coplanar merges ('<a
|
||||
href="qh-optc.htm#An">A-n</a>') are not performed. Concavity
|
||||
testing is delayed until a merge occurs.</p>
|
||||
|
||||
<p>After the hull is built, all coplanar merges are performed
|
||||
(defined by '<a href="qh-optc.htm#Cn">C-n</a>' and '<a
|
||||
href="qh-optc.htm#An">A-n</a>'), then post-merges are performed
|
||||
(defined by '<a href="qh-optc.htm#Cn2">Cn</a>' and '<a
|
||||
href="qh-optc.htm#An2">An</a>'). If facet progress is logged ('<a
|
||||
href="qh-optt.htm#TFn">TFn</a>'), Qhull reports each phase and
|
||||
prints intermediate summaries and statistics ('<a
|
||||
href="qh-optt.htm#Ts">Ts</a>'). </p>
|
||||
|
||||
<p>Without 'Qx' in 5-d and higher, options '<a
|
||||
href="qh-optc.htm#Cn">C-n</a>' and '<a href="qh-optc.htm#An">A-n</a>'
|
||||
may merge too many facets. Since redundant vertices are not
|
||||
removed effectively, facets become increasingly wide. </p>
|
||||
|
||||
<p>Option 'Qx' may report a wide facet. With 'Qx', coplanar
|
||||
facets are not merged. This can produce a "dent" in an
|
||||
intermediate hull. If a point is partitioned into a dent and it
|
||||
is below the surrounding facets but above other facets, one or
|
||||
more wide facets will occur. In practice, this is unlikely. To
|
||||
observe this effect, run Qhull with option '<a href="#Q6">Q6</a>'
|
||||
which doesn't pre-merge concave facets. A concave facet makes a
|
||||
large dent in the intermediate hull.</p>
|
||||
|
||||
<p>Option 'Qx' may set an outer plane below one of the input
|
||||
points. A coplanar point may be assigned to the wrong facet
|
||||
because of a "dent" in an intermediate hull. After
|
||||
constructing the hull, Qhull double checks all outer planes with
|
||||
qh_check_maxout in <tt>poly2.c </tt>. If a coplanar point is
|
||||
assigned to the wrong facet, qh_check_maxout may reach a local
|
||||
maximum instead of locating all coplanar facets. This appears to
|
||||
be unlikely.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Qz">Qz - add a
|
||||
point-at-infinity for Delaunay triangulations</a></h3>
|
||||
|
||||
<p>Option 'Qz' adds a point above the paraboloid of lifted sites
|
||||
for a Delaunay triangulation. It allows the Delaunay
|
||||
triangulation of cospherical sites. It reduces precision errors
|
||||
for nearly cospherical sites.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q0">Q0 - no merging with C-0
|
||||
and Qx</a></h3>
|
||||
|
||||
<p>Turn off default merge options '<a href="qh-optc.htm#C0">C-0</a>'
|
||||
and '<a href="#Qx">Qx</a>'. </p>
|
||||
|
||||
<p>With 'Q0' and without other pre-merge options, Qhull ignores
|
||||
precision issues while constructing the convex hull. This may
|
||||
lead to precision errors. If so, a descriptive warning is
|
||||
generated. See <a href="qh-impre.htm">Precision issues</a>.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q1">Q1 - sort merges by type
|
||||
instead of angle </a></h3>
|
||||
|
||||
<p>Qhull sorts the coplanar facets before picking a subset of the
|
||||
facets to merge. It merges concave and flipped facets first. Then
|
||||
it merges facets that meet at a steep angle. With 'Q1', Qhull
|
||||
sorts merges by type (coplanar, angle coplanar, concave) instead
|
||||
of by angle. This may make the facets wider. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q2">Q2 - merge all non-convex
|
||||
at once instead of independent sets </a></h3>
|
||||
|
||||
<p>With 'Q2', Qhull merges all facets at once instead of
|
||||
performing merges in independent sets. This may make the facets
|
||||
wider. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q3">Q3 - do not merge
|
||||
redundant vertices </a></h3>
|
||||
|
||||
<p>With 'Q3', Qhull does not remove redundant vertices. In 6-d
|
||||
and higher, Qhull never removes redundant vertices (since
|
||||
vertices are highly interconnected). Option 'Q3' may be faster,
|
||||
but it may result in wider facets. Its effect is easiest to see
|
||||
in 3-d and 4-d.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q4">Q4 - avoid merging </a>old
|
||||
facets into new facets</h3>
|
||||
|
||||
<p>With 'Q4', Qhull avoids merges of an old facet into a new
|
||||
facet. This sometimes improves facet width and sometimes makes it
|
||||
worse. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q5">Q5 - do not correct outer
|
||||
planes at end of qhull </a></h3>
|
||||
|
||||
<p>When merging facets or approximating a hull, Qhull tests
|
||||
coplanar points and outer planes after constructing the hull. It
|
||||
does this by performing a directed search (qh_findbest in <tt>geom.c</tt>).
|
||||
It includes points that are just inside the hull. </p>
|
||||
|
||||
<p>With options 'Q5' or '<a href="qh-optp.htm#Po">Po</a>', Qhull
|
||||
does not test outer planes. The maximum outer plane is used
|
||||
instead. Coplanar points ('<a href="#Qc">Qc</a>') are defined by
|
||||
'<a href="qh-optc.htm#Un">Un</a>'. An input point may be outside
|
||||
of the maximum outer plane (this appears to be unlikely). An
|
||||
interior point may be above '<a href="qh-optc.htm#Un">Un</a>'
|
||||
from a hyperplane.</p>
|
||||
|
||||
<p>Option 'Q5' may be used if outer planes are not needed. Outer
|
||||
planes are needed for options '<a href="qh-opto.htm#s">s</a>', '<a
|
||||
href="qh-optg.htm#G">G</a>', '<a href="qh-optg.htm#Go">Go </a>',
|
||||
'<a href="qh-optf.htm#Fs">Fs</a>', '<a href="qh-optf.htm#Fo">Fo</a>',
|
||||
'<a href="qh-optf.htm#FF">FF</a>', and '<a href="qh-opto.htm#f">f</a>'.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q6">Q6 - do not pre-merge
|
||||
concave or coplanar facets </a></h3>
|
||||
|
||||
<p>With 'Q6', Qhull does not pre-merge concave or coplanar
|
||||
facets. This demonstrates the effect of "dents" when
|
||||
using '<a href="#Qx">Qx</a>'. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q7">Q7 - depth-first
|
||||
processing instead of breadth-first </a></h3>
|
||||
|
||||
<p>With 'Q7', Qhull processes facets in depth-first order instead
|
||||
of breadth-first order. This may increase the locality of
|
||||
reference in low dimensions. If so, Qhull may be able to use
|
||||
virtual memory effectively. </p>
|
||||
|
||||
<p>In 5-d and higher, many facets are visible from each
|
||||
unprocessed point. So each iteration may access a large
|
||||
proportion of allocated memory. This makes virtual memory
|
||||
ineffectual. Once real memory is used up, Qhull will spend most
|
||||
of its time waiting for I/O.</p>
|
||||
|
||||
<p>Under 'Q7', Qhull runs slower and the facets may be wider. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q8">Q8 - ignore near-interior
|
||||
points </a></h3>
|
||||
|
||||
<p>With 'Q8' and merging, Qhull does not process interior points
|
||||
that are near to a facet (as defined by qh_RATIOnearInside in
|
||||
user.h). This avoids partitioning steps. It may miss a coplanar
|
||||
point when adjusting outer hulls in qh_check_maxout(). The best
|
||||
value for qh_RATIOnearInside is not known. Options 'Q8 <a
|
||||
href="#Qc">Qc</a>' may be sufficient. </p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q9">Q9 - process furthest of
|
||||
furthest points </a></h3>
|
||||
|
||||
<p>With 'Q9', Qhull processes the furthest point of all outside
|
||||
sets. This may reduce precision problems. The furthest point of
|
||||
all outside sets is not necessarily the furthest point from the
|
||||
convex hull.</p>
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q10">Q10 - no special processing
|
||||
for narrow distributions</a></h3>
|
||||
|
||||
<p>With 'Q10', Qhull does not special-case narrow distributions.
|
||||
See <a href=qh-impre.htm#limit>Limitations of merged facets</a> for
|
||||
more information.
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q11">Q11 - copy normals and recompute
|
||||
centrums for
|
||||
tricoplanar facets</a></h3>
|
||||
|
||||
Option '<a href="#Qt">Qt</a>' triangulates non-simplicial facets
|
||||
into "tricoplanar" facets.
|
||||
Normally tricoplanar facets share the same normal, centrum, and
|
||||
Voronoi vertex. They can not be merged or replaced. With
|
||||
option 'Q11', Qhull duplicates the normal and Voronoi vertex.
|
||||
It recomputes the centrum.
|
||||
|
||||
<p>Use 'Q11' if you use the Qhull library to add points
|
||||
incrementally and call qh_triangulate() after each point.
|
||||
Otherwise, Qhull will report an error when it tries to
|
||||
merge and replace a tricoplanar facet.
|
||||
|
||||
<p>With sufficient merging and new points, option 'Q11' may
|
||||
lead to precision problems such
|
||||
as duplicate ridges and concave facets. For example, if qh_triangulate()
|
||||
is added to qh_addpoint(), RBOX 1000 s W1e-12 t1001813667 P0 | QHULL d Q11 Tv,
|
||||
reports an error due to a duplicate ridge.
|
||||
|
||||
<h3><a href="#qhull">»</a><a name="Q12">Q12 - do not error
|
||||
on wide merge due to duplicate ridge and nearly coincident points</a></h3>
|
||||
|
||||
<p>In 3-d and higher Delaunay Triangulations or 4-d and higher convex hulls, multiple,
|
||||
nearly coincident points may lead to very wide facets. An error is reported if a
|
||||
merge across a duplicate ridge would increase the facet width by 100x or more.
|
||||
|
||||
<p>Use option 'Q12' to log a warning instead of throwing an error.
|
||||
|
||||
<p>For Delaunay triangulations, a bounding box may alleviate this error (e.g., <tt>rbox 500 C1,1E-13 t c G1 | qhull d</tt>).
|
||||
This avoids the ill-defined edge between upper and lower convex hulls.
|
||||
The problem will be fixed in a future release of Qhull.
|
||||
|
||||
<p>To demonstrate the problem, use rbox option 'Cn,r,m' to generate nearly coincident points.
|
||||
For more information, see "Nearly coincident points on an edge"
|
||||
in <a href="qh-impre.htm#limit"</a>Nearly coincident points on an edge</a>.
|
||||
|
||||
<!-- 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>
|
||||
• <a href="qh-quick.htm#options">Options</a>
|
||||
• <a href="qh-opto.htm#output">Output</a>
|
||||
• <a href="qh-optf.htm#format">Formats</a>
|
||||
• <a href="qh-optg.htm#geomview">Geomview</a>
|
||||
• <a href="qh-optp.htm#print">Print</a>
|
||||
• <a href="qh-optq.htm#qhull">Qhull</a>
|
||||
• <a href="qh-optc.htm#prec">Precision</a>
|
||||
• <a href="qh-optt.htm#trace">Trace</a>
|
||||
• <a href="../src/libqhull_r/index.htm">Functions</a></p>
|
||||
<!-- 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>
|
||||