Merge remote-tracking branch 'remotes/origin/master' into support_improvements

This commit is contained in:
bubnikv 2018-08-30 15:24:49 +02:00
commit 3c0060d9ac
376 changed files with 114635 additions and 4946 deletions

View file

@ -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()

View 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)

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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();

View file

@ -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));

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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));

View file

@ -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. */

View file

@ -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);

View 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);
}

View file

@ -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"

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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()

View file

@ -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})

View file

@ -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)

View file

@ -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");

View file

@ -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>

View file

@ -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)

View file

@ -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;
//}

View file

@ -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

View file

@ -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();
}
};

View file

@ -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

View file

@ -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!");

View file

@ -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();
});
}
};

View 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

View file

@ -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...>();
}

View file

@ -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 );

View file

@ -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()) };

View file

@ -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++;
}

View file

@ -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++;
}
}

View file

@ -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;

View file

@ -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);
//}
}

View file

@ -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;
};
}

View file

@ -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 {

View file

@ -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)

View file

@ -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 = "&quot;"; break;
case '\'': replacement = "&apos;"; break;
case '&': replacement = "&amp;"; break;
case '<': replacement = "&lt;"; break;
case '>': replacement = "&gt;"; 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) {

View file

@ -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 &copy : 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(),

View file

@ -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 {

View file

@ -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);

View file

@ -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(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers.end())

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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(); }

View file

@ -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

View file

@ -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();

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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.

View file

@ -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);

View 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

View file

@ -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
{

View file

@ -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);

View file

@ -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 &region)
{
@ -1233,5 +1240,4 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
}
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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()
{

View file

@ -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:

View file

@ -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); }

View file

@ -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;

View file

@ -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 = "&quot;"; break;
case '\'': replacement = "&apos;"; break;
case '&': replacement = "&amp;"; break;
case '<': replacement = "&lt;"; break;
case '>': replacement = "&gt;"; break;
default: break;
}
text.replace(pos, 1, replacement);
pos += replacement.size();
}
return text;
}
}; // namespace Slic3r

View file

@ -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
View file

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

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

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

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

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

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

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

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

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because it is too large Load diff

693
xs/src/qhull/html/qh-eg.htm Normal file
View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To: </b><a href="#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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h2><a href="#TOP">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</a>2-d and 3-d examples</h2>
<h3><a href="#2d">&#187;</a><a name="01">rbox c D3 | qconvex G
&gt;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">&#187;</a><a name="02">rbox c d G3.0 | qconvex G
&gt;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">&#187;</a><a name="03">rbox s 100 D3 | qconvex G
&gt;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 &gt;eg.99</i>.</p>
<h3><a href="#2d">&#187;</a><a name="04">rbox s 100 D2 | qconvex G
&gt;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 &amp; Shamos <a
href="index.htm#pre-sha85">'85</a>]. It was the model for
Qhull.</p>
<h3><a href="#2d">&#187;</a><a name="05">rbox 10 l | qconvex G
&gt;eg.05.spiral </a></h3>
<p>One rotation of a spiral.</p>
<h3><a href="#2d">&#187;</a><a name="06">rbox 1000 D2 | qconvex C-0.03
Qc Gapcv &gt;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">&#187;</a><a name="07">rbox 1000 D3 | qconvex G
&gt;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">&#187;</a><a name="08a">rbox c G0.4 s 500 | qconvex G
&gt;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">&#187;</a><a name="08b">rbox d G0.6 s 500 | qconvex G
&gt;eg.08b.diamond.sphere </a></h3>
<p>This is a combination of the diamond distribution and the
sphere.</p>
<h3><a href="#2d">&#187;</a><a name="09">rbox 100 L3 G0.5 s | qconvex
G &gt;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">&#187;</a>How Qhull adds a point</h2>
<h3><a href="#how">&#187;</a><a name="10a">rbox 100 s P0.5,0.5,0.5 |
qconvex Ga QG0 &gt;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">&#187;</a><a name="10b">rbox 100 s
P0.5,0.5,0.5|qconvex Ga QG-0 &gt;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">&#187;</a><a name="10c">rbox 100 s P0.5,0.5,0.5 |
qconvex PG Ga QG0 &gt;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">&#187;</a><a name="10d">rbox 100 s P0.5,0.5,0.5 |
qconvex Ga QV0 PgG &gt;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">&#187;</a><a name="10e">rbox 100 s P0.5,0.5,0.5 |
qconvex Ga &gt;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">&#187;</a><a name="14">rbox 100 s P0.5,0.5,0.5 |
qhull Ga QV0g Q0 &gt;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">&#187;</a>Triangulated output or joggled input</h2>
<h3><a href="#joggle">&#187;</a><a name="15a">rbox 500 W0 | qconvex QR0 Qc Gvp &gt;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">&#187;</a><a name="15b">rbox 500 W0 | qconvex QR0 Qt Qc Gvp &gt;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">&#187;</a><a name="15c">rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp &gt;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">&#187;</a>Delaunay and Voronoi diagrams</h2>
<h3><a href="#delaunay">&#187;</a><a name="17a">qdelaunay Qt
&lt;eg.data.17 GnraD2 &gt;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">&#187;</a><a name="17b">qdelaunay &lt;eg.data.17
GnraD2 &gt;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">&#187;</a><a name="17c">qdelaunay &lt;eg.data.17
Ga &gt;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">&#187;</a><a name="17d">qvoronoi QJ
&lt;eg.data.17 Gna &gt;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">&#187;</a><a name="17e">qvoronoi &lt;eg.data.17
Gna &gt;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 &lt;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">&#187;</a><a name="17f"> rbox c G0.1 d |
qdelaunay Gt Qz &lt;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 &quot;at infinity&quot;. This avoids a degenerate
input due to cospherical points.</p>
<h3><a href="#delaunay">&#187;</a><a name="18a">rbox 10 D2 d | qdelaunay
Qu G &gt;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">&#187;</a><a name="18b">rbox 10 D2 d | qdelaunay
Qu Pd2 G &gt;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">&#187;</a><a name="18c">rbox 10 D2 d | qvoronoi
Qu Gv &gt;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">&#187;</a><a name="19">rbox 10 D3 | qvoronoi QV5
p | qconvex G &gt;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">&#187;</a>Facet merging for imprecision</h2>
<h3><a href="#merge">&#187;</a><a name="20">rbox r s 20 Z1 G0.2 |
qconvex G &gt;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">&#187;</a><a name="21a">rbox 200 s | qhull Q0
R0.01 Gav Po &gt;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">&#187;</a><a name="21b">rbox 200 s | qconvex Qc
R0.01 Gpav &gt;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">&#187;</a><a name="22a">rbox 1000 s| qconvex C0.01
Qc Gcrp &gt;eg.22a.merge.sphere.01</a></h3>
<h3><a href="#merge">&#187;</a><a name="22b">rbox 1000 s| qconvex
C-0.01 Qc Gcrp &gt;eg.22b.merge.sphere.-01</a></h3>
<h3><a href="#merge">&#187;</a><a name="22c">rbox 1000 s| qconvex C0.05
Qc Gcrpv &gt;eg.22c.merge.sphere.05</a></h3>
<h3><a href="#merge">&#187;</a><a name="22d">rbox 1000 s| qconvex
C-0.05 Qc Gcrpv &gt;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">&#187;</a><a name="23">rbox 1000 | qconvex s
Gcprvah C0.1 Qc &gt;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">&#187;</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">&#187;</a><a name="24">rbox 5000 D4 | qconvex s GD0v
Pd0:0.5 C-0.02 C0.1 &gt;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">&#187;</a><a name="30">rbox 5000 D4 | qconvex s
C-0.02 C0.1 Gh &gt;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">&#187;</a><a name="31">rbox 20 D3 | qdelaunay G
&gt;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">&#187;</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">&#187;</a>Halfspace intersections</h2>
<h3><a href="#half">&#187;</a><a name="33a">rbox 10 r s Z1 G0.3 |
qconvex G &gt;eg.33a.cone </a></h3>
<h3><a href="#half">&#187;</a><a name="33b">rbox 10 r s Z1 G0.3 |
qconvex FV n | qhalf G &gt;eg.33b.cone.dual</a></h3>
<h3><a href="#half">&#187;</a><a name="33c">rbox 10 r s Z1 G0.3 |
qconvex FV n | qhalf Fp | qconvex G &gt;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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To: </b><a href="#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

File diff suppressed because it is too large Load diff

View 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.,
&quot;The Quickhull algorithm for convex hulls,&quot; <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
&quot;bcc32 -WX -w- -O2-e -erbox -lc rbox.c&quot;. 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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To: </b><a href="#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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<h2><a href="#TOP">&#187;</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">&#187;</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 (&quot;flipped&quot; 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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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 &quot;dents&quot; 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., &quot;(3.4x)&quot;. 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">&#187;</a><a name="injoggle">Joggled input</a></h2>
<p>Joggled input is a simple work-around for precision problems
in computational geometry [&quot;joggle: to shake or jar
slightly,&quot; 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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a><br>
<b>To:</b> <a href="#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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="prec">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>&nbsp;</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>&nbsp;</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">&#187;</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">&#187;</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">&#187;</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' (&quot;_zero-centrum&quot;) 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">&#187;</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">&#187;</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">&#187;</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 &quot;max. distance of a new vertex to a
facet&quot; (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">&#187;</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">&#187;</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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="format">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
<h2>Additional input &amp; 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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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 &lt;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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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&lt;0</i>
is its offset, the halfspace intersection occurs at
[x/-b,y/-b,z/-b] plus the interior point. If <i>b&gt;=0</i>, the
halfspace intersection is unbounded. </p>
<h3><a href="#format">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="geomview">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="output">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>&nbsp;</dt>
<dt>&nbsp;</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>&nbsp;</dt>
</dl>
<hr>
<h3><a href="#output">&#187;</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">&#187;</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">&#187;</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: &quot;<tt>list= &lt;&lt; &lt;outputfilename&gt; </tt>&quot;.
If the object is 2-d, it can be visualized by &quot;<tt>Show[Graphics[list]]
</tt>&quot;. For 3-d objects the command is &quot;<tt>Show[Graphics3D[list]]
</tt>&quot;. Now the object can be manipulated by commands of the
form <tt>&quot;Show[%, &lt;parametername&gt; -&gt;
&lt;newvalue&gt;]</tt>&quot;. </p>
<p>For Delaunay triangulation orthogonal projection is better.
This can be specified, for example, by &quot;<tt>BoxRatios:
Show[%, BoxRatios -&gt; {1, 1, 1e-8}]</tt>&quot;. To see the
meaningful side of the 3-d object used to visualize 2-d Delaunay,
you need to change the viewpoint: &quot;<tt>Show[%, ViewPoint
-&gt; {0, 0, -1}]</tt>&quot;. 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">&#187;</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
&lt;= -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 &quot;begin&quot;, the
number of facets, the dimension (plus one), the keyword
&quot;real&quot;, 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 &quot;end&quot;. </p>
<h3><a href="#output">&#187;</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">&#187;</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 &quot;begin&quot;. The second line is the
number of vertices, the dimension plus one, and &quot;real&quot;.
The vertices follow with a leading &quot;1&quot;. Output ends
with &quot;end&quot;. </p>
<h3><a href="#output">&#187;</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
(&quot;good&quot;) 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="format">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
<h2>Print options</h2>
<blockquote>
<dl compact>
<dt>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</dt>
<dd><b>Select</b></dd>
<dt><a href="#Pdk">Pdk:n</a></dt>
<dd>print facets with normal[k] &gt;= n (default 0.0)</dd>
<dt><a href="#PDk">PDk:n</a></dt>
<dd>print facets with normal[k] &lt;= 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">&#187;</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">&#187;</a><a name="Pdk">Pdk:n - print facet if
normal[k] &gt;= n </a></h3>
<p>For a given output, print only those facets with <i>normal[k] &gt;= n</i>
and <i>drop</i> the others. For example, 'Pd0:0.5' prints facets with <i>normal[0]
&gt;= 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">&#187;</a><a name="PDk">PDk:n - print facet if
normal[k] &lt;= n</a></h3>
<p>For a given output, print only those facets with <i>normal[k] &lt;= n</i>
and <i>drop</i> the others.
For example, 'PD0:0.5' prints facets with <i>normal[0]
&lt;= 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">&#187;</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">&#187;</a><a name="Pg">Pg - print good facets </a></h3>
<p>Qhull can mark facets as &quot;good&quot;. 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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

View 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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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 &copy; 1995-2015 C.B. Barber</b></p>
<hr>
<p><a href="index.htm#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
<a name="qhull">&#149;</a> <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></p>
<h2>Qhull control options</h2>
<dl compact>
<dt>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</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>&nbsp;</dt>
<dt>&nbsp;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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 &lt; 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">&#187;</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">&#187;</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">&#187;</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">&#187;</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 &quot;hyperplanes constructed&quot; and
&quot;distance tests&quot;. Qhull does not report CPU time
because the randomization is inefficient. </p>
<h3><a href="#qhull">&#187;</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&gt;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&lt;-1</em>,
don't rotate but use <em>n</em> as the random number seed. </p>
<h3><a href="#qhull">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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&lt;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">&#187;</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 &quot;exact&quot; 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 &quot;dent&quot; 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 &quot;dent&quot; 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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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 &quot;dents&quot; when
using '<a href="#Qx">Qx</a>'. </p>
<h3><a href="#qhull">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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">&#187;</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>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="../src/libqhull_r/index.htm">Functions</a></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>

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