739088af9f
- aggiornamento versione.
1245 lines
42 KiB
C++
1245 lines
42 KiB
C++
/*****************************************************************************/
|
|
/* */
|
|
/* Copyright (C) 2000-2025 M. Held */
|
|
/* */
|
|
/* This code is not in the public domain. All rights reserved! Please make */
|
|
/* sure to read the full copyright statement contained in "README.txt" or in */
|
|
/* the "main" file of this code, such as "main.cc". */
|
|
/* */
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* Written by: Martin Held */
|
|
/* */
|
|
/* E-Mail: held@cs.sbg.ac.at */
|
|
/* Fax Mail: (+43 662) 8044-611 */
|
|
/* Voice Mail: (+43 662) 8044-6304 */
|
|
/* Snail Mail: Martin Held */
|
|
/* FB Informatik */
|
|
/* Universitaet Salzburg */
|
|
/* A-5020 Salzburg, Austria */
|
|
/* */
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* this file contains sample routines for computing VD-based offsets. */
|
|
/* */
|
|
/* please_ understand that those routines are meant as nothing but as */
|
|
/* samples to show how the VD can be used to compute offsets, and to */
|
|
/* highlight the use of the macros necessary for accessing the VD. those */
|
|
/* routines are _not_ intended to be regarded as a fully fledged code for */
|
|
/* generating tool paths, or for performing sophisticated multiple buffering */
|
|
/* in a GIS application. frankly stated, there are just too many diverse */
|
|
/* requirements with respect to offsetting to make it feasible for me to */
|
|
/* offer a general-purpose offsetting code without making it utterly */
|
|
/* complicated... */
|
|
/* */
|
|
/* that is, if the offsetting scheme implemented does not fit your needs, */
|
|
/* and if it doesn't look like you could use "exterior application macros" */
|
|
/* to influence the behavior of my code from outside to get it to generate */
|
|
/* offsets the way you want them generated, then, please, use my routines as */
|
|
/* a blue print for your own offsetting code, rather than to ask me to add */
|
|
/* specific features to my offsetting code in order to customize it for your */
|
|
/* purposes. needless to say, you are encouraged to use my macros for */
|
|
/* accessing my data structures. (i will strive to keep the I/O specs of all */
|
|
/* my access macros!) */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
/* */
|
|
/* get standard libraries */
|
|
/* */
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
|
|
/* */
|
|
/* get my header files */
|
|
/* */
|
|
#include "fpkernel.h"
|
|
#include "vronivector.h"
|
|
#include "vroni_object.h"
|
|
#include "defs.h"
|
|
#include "offset.h"
|
|
#include "numerics.h"
|
|
#include "io_dxf.h"
|
|
|
|
|
|
void vroniObject::SetMaxOffset(double_arg t)
|
|
{
|
|
max_t_offset = t;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
vr_bool vroniObject::InOffsetData(int I)
|
|
{
|
|
return ((I >= 0) && (I < num_offset_data) &&
|
|
(num_offset_data <= max_num_offset_data));
|
|
}
|
|
|
|
|
|
vr_bool vroniObject::InOffsetList(int I)
|
|
{
|
|
return ((I >= 0) && (I < num_offset_list) &&
|
|
(num_offset_list <= max_num_offset_list));
|
|
}
|
|
|
|
|
|
|
|
off_list* vroniObject::GetOffsetList(int i)
|
|
{
|
|
if (InOffsetList(i))
|
|
return &(offset_list[i]);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
off_data* vroniObject::GetOffsetData(int i)
|
|
{
|
|
if (InOffsetData(i))
|
|
return &(offset_data[i]);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::ResetOffsetData(void)
|
|
{
|
|
num_offset_list = 0;
|
|
num_offset_data = 0;
|
|
cur_offset_list = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
/* this function checks an offset arc and replaces it by a line segment if */
|
|
/* its orientation is wrong or problematic, e.g., since its arc length is */
|
|
/* small. */
|
|
/* */
|
|
vr_bool vroniObject::ScrutinizeArc(coord start, coord end, coord center,
|
|
vr_bool *ori, double eps,
|
|
vr_bool not_isolated)
|
|
{
|
|
coord vec_s, vec_e;
|
|
double det;
|
|
|
|
if (!not_isolated) return true;
|
|
|
|
if (eq(PntPntDist(start, end), eps)) {
|
|
/* */
|
|
/* this is a tiny arc that is filtered. (it will be replaced by a line */
|
|
/* segment.) */
|
|
/* */
|
|
return false;
|
|
}
|
|
|
|
if (*ori) det = VecDet(start, end, center);
|
|
else det = -VecDet(start, end, center);
|
|
|
|
if (eq(det, eps)) {
|
|
/* */
|
|
/* this arc is either a (correct) half-circle or a rather tiny arc */
|
|
/* */
|
|
vec_s = VecSub(start, center);
|
|
vec_e = VecSub(end, center);
|
|
if (VecDotProd(vec_e, vec_s) < 0.0) {
|
|
/* */
|
|
/* this arc is roughly a half circle */
|
|
/* */
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (det < 0.0) {
|
|
/* */
|
|
/* this arc seems to have the wrong orientation, for unknown reasons. */
|
|
/* we adjust the orientation. */
|
|
/* */
|
|
*ori = ! *ori;
|
|
VD_IO_Warning("ScrutinizeArc() - reversed orientation of arc!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
void vroniObject::FreeOffsetData(void)
|
|
{
|
|
offset_data.clear();
|
|
offset_list.clear();
|
|
|
|
num_offset_data = 0;
|
|
max_num_offset_data = 0;
|
|
num_offset_list = 0;
|
|
max_num_offset_list = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::ComputeIsolatedOffsets(double_arg t0, double_arg d_off)
|
|
{
|
|
int number, i, e, e0, n1, n2;
|
|
double min_t, tn2, x = 0.0, t;
|
|
coord w;
|
|
|
|
number = num_pnts - 2;
|
|
|
|
for (i = 2; i < number; ++i) {
|
|
if (!HasIncidentSite(i) && !IsDeletedPnt(i)) {
|
|
/* */
|
|
/* find minimum clearance of all Voronoi nodes of this cell */
|
|
/* */
|
|
e0 = GetVDPtr(i, PNT);
|
|
if (e0 == NIL) {
|
|
VD_Warning("ComputeIsolatedOffsets - null VD pointer!\n");
|
|
}
|
|
else {
|
|
e = e0;
|
|
if (IsLftSite(e, i, PNT)) {
|
|
n1 = GetEndNode(e);
|
|
}
|
|
else {
|
|
assert(IsRgtSite(e, i, PNT));
|
|
n1 = GetStartNode(e);
|
|
}
|
|
min_t = GetNodeParam(n1);
|
|
|
|
do {
|
|
n2 = GetOtherNode(e, n1);
|
|
tn2 = GetNodeParam(n2);
|
|
min_t = VroniMin(min_t, tn2);
|
|
n1 = n2;
|
|
e = GetCCWEdge(e, n1);
|
|
} while (e != e0);
|
|
|
|
/* */
|
|
/* compute concentric offset circles */
|
|
/* */
|
|
t = t0;
|
|
x = GetPntCoords(i).x;
|
|
w.y = GetPntCoords(i).y;
|
|
|
|
while (t <= min_t) {
|
|
NewOffsetCurve;
|
|
w.x = x + t;
|
|
StoreOffsetData(i, PNT, w);
|
|
w.x = x - t;
|
|
StoreOffsetData(i, PNT, w);
|
|
SetOffsetNumber;
|
|
#ifdef VISUAL_TEST_OFFSETS
|
|
if (small_increment) {
|
|
small_increment = false;
|
|
t *= 10.0;
|
|
}
|
|
else {
|
|
t += d_off;
|
|
}
|
|
#else
|
|
t += d_off;
|
|
#endif
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncIsoOffset;
|
|
}
|
|
#ifdef VISUAL_TEST_OFFSETS
|
|
small_increment = true;
|
|
#endif
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncIsoOffsetDone;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
/* this is the main function for my sample implementation of VD-based */
|
|
/* offsetting. please see API_ComputeOff() for an explanation of the */
|
|
/* parameters. also, please note that the offsets computed are stored in */
|
|
/* offset_data, and those records are not reset automatically when this */
|
|
/* function is called. */
|
|
/* */
|
|
void vroniObject::ComputeOff(int step_size, vr_bool time, char off_file[],
|
|
vr_bool write_off, vr_bool dxf_format,
|
|
double_arg t_offset, double_arg d_offset,
|
|
vr_bool *finished,
|
|
vr_bool left_offset, vr_bool right_offset)
|
|
{
|
|
double t1, t2;
|
|
int i, j, not_needed;
|
|
vr_bool delete_it, unrestricted;
|
|
|
|
#ifdef GRAPHICS
|
|
int cntr = 0;
|
|
#endif
|
|
|
|
assert(GetVDStatus());
|
|
if (GetNodesSqdFlag()) TakeSquareOfNodes();
|
|
if (!GetDeg2Flag()) InsertDegreeTwoNodes();
|
|
|
|
i = GetVDIdentifier();
|
|
if (offset_VD_ID != i) {
|
|
offset_VD_ID = i;
|
|
e_data_init = true;
|
|
}
|
|
|
|
if (e_data_init) {
|
|
/* */
|
|
/* first call to this function. allocate memory and initialize the */
|
|
/* flags for the edge data. */
|
|
/* */
|
|
VD_Info("...computing offset curves -- ");
|
|
if (time) (void)elapsed();
|
|
|
|
t = ScaleV(t_offset);
|
|
d_off = ScaleV(d_offset);
|
|
if (le(d_off, ZERO)) d_off = max_t_offset;
|
|
#ifdef GRAPHICS
|
|
if (graphics) ResetCurBuffer();
|
|
#else
|
|
step_size = INT_MAX;
|
|
#endif
|
|
if (t >= max_t_offset) {
|
|
*finished = true;
|
|
VD_Warning("ComputeOff() - requested offset is too large!");
|
|
}
|
|
else if (le(t, ZERO)) {
|
|
*finished = true;
|
|
VD_Warning("ComputeOff() - requested offset is too small!");
|
|
}
|
|
else {
|
|
*finished = false;
|
|
data_written = false;
|
|
unrestricted = !(left_offset || right_offset);
|
|
|
|
/* */
|
|
/* allocate memory and initialize the flags for the edge data. */
|
|
/* */
|
|
InitializeEdgeData(unrestricted, left_offset, false, ¬_needed);
|
|
|
|
/* */
|
|
/* handle all offsets around isolated pnts */
|
|
/* */
|
|
if (isolated_pnts && unrestricted)
|
|
ComputeIsolatedOffsets(t, d_off);
|
|
cpu_time_off = 0.0;
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffsetInit;
|
|
}
|
|
}
|
|
else {
|
|
if (time) (void)elapsed();
|
|
t += d_off;
|
|
if (t >= max_t_offset) *finished = true;
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffsetRepeat;
|
|
}
|
|
|
|
|
|
while (!*finished) {
|
|
/* */
|
|
/* mark all edges that will be intersected by an offset curve with */
|
|
/* offset t. */
|
|
/* */
|
|
j = -1;
|
|
AdvanceActiveEdge(i, j, false);
|
|
while (i != NIL) {
|
|
delete_it = false;
|
|
if (GetEdgeFlagNew(i)) {
|
|
GetEdgeParam(i, &t1, &t2);
|
|
if (t >= VroniMin(t1, t2)) {
|
|
if (t < VroniMax(t1, t2)) {
|
|
/* */
|
|
/* this edge will be intersected by an offset curve. let's */
|
|
/* compute this offset curve. */
|
|
/* */
|
|
ComputeOffset(i, t);
|
|
SetEdgeFlagNew(i, true);
|
|
}
|
|
else {
|
|
/* */
|
|
/* for increasing values of t, this edge won't be */
|
|
/* intersected by any offset curve any more. */
|
|
/* */
|
|
delete_it = true;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* */
|
|
/* this edge has already been intersected by an offset curve */
|
|
/* for the current offset. */
|
|
/* */
|
|
SetEdgeFlagNew(i, true);
|
|
}
|
|
AdvanceActiveEdge(i, j, delete_it);
|
|
}
|
|
|
|
#ifdef GRAPHICS
|
|
if (graphics) {
|
|
++cntr;
|
|
if (cntr >= step_size) {
|
|
AddOffsetsToBuffer();
|
|
cntr = 0;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
/* */
|
|
/* all offsets for offset distance t * scale_factor have been */
|
|
/* computed. */
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffLoops;
|
|
|
|
#ifdef VISUAL_TEST_OFFSETS
|
|
if (small_increment) {
|
|
small_increment = false;
|
|
t *= 10.0;
|
|
}
|
|
else {
|
|
t += d_off;
|
|
}
|
|
#else
|
|
t += d_off;
|
|
#endif
|
|
if (t >= max_t_offset) *finished = true;
|
|
}
|
|
if (time) cpu_time_off += elapsed();
|
|
|
|
if (*finished) {
|
|
VD_Info("done!\n");
|
|
#ifdef GRAPHICS
|
|
if (graphics) {
|
|
AddOffsetsToBuffer();
|
|
}
|
|
#endif
|
|
if (!data_written) {
|
|
data_written = true;
|
|
if (write_off) {
|
|
VD_Info("...writing the offset data -- ");
|
|
if (dxf_format) WriteOffsetsDXF(off_file);
|
|
else WriteOffsets(off_file);
|
|
VD_Info("done!\n");
|
|
}
|
|
}
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffDone;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::EvaluateBisectorData(int i, /* bisector */
|
|
double_arg t, /* boundary clearance */
|
|
coord *w) /* offset point */
|
|
{
|
|
int lft, rgt;
|
|
t_site t_lft, t_rgt;
|
|
enum {LINE, PARABOLA, HYPERBOLA, HYPERELL} conic;
|
|
|
|
GetLftSiteData(i, &lft, &t_lft);
|
|
GetRgtSiteData(i, &rgt, &t_rgt);
|
|
|
|
if ((t_lft == PNT) && (t_rgt == PNT)) {
|
|
conic = HYPERBOLA;
|
|
}
|
|
else if ((t_lft == PNT) || (t_lft == ARC)) {
|
|
if ((t_rgt == PNT) || (t_rgt == ARC)) {
|
|
if (t_lft == PNT) {
|
|
assert(t_rgt == ARC);
|
|
assert(InArcsList(rgt));
|
|
if (IsArcStartPnt(rgt, lft) || IsArcEndPnt(rgt, lft))
|
|
conic = LINE;
|
|
else
|
|
conic = HYPERELL;
|
|
}
|
|
else if (t_rgt == PNT) {
|
|
assert(t_lft == ARC);
|
|
assert(InArcsList(lft));
|
|
if (IsArcStartPnt(lft, rgt) || IsArcEndPnt(lft, rgt))
|
|
conic = LINE;
|
|
else
|
|
conic = HYPERELL;
|
|
}
|
|
else {
|
|
conic = HYPERELL;
|
|
}
|
|
}
|
|
else {
|
|
assert(t_rgt == SEG);
|
|
assert(InSegsList(rgt));
|
|
if ((t_lft == PNT) &&
|
|
(IsSegStartPnt(rgt, lft) || IsSegEndPnt(rgt, lft)))
|
|
conic = LINE;
|
|
else
|
|
conic = PARABOLA;
|
|
}
|
|
}
|
|
else {
|
|
if ((t_rgt == PNT) || (t_rgt == ARC)) {
|
|
assert(t_lft == SEG);
|
|
assert(InSegsList(lft));
|
|
if ((t_rgt == PNT) &&
|
|
(IsSegStartPnt(lft, rgt) || IsSegEndPnt(lft, rgt)))
|
|
conic = LINE;
|
|
else
|
|
conic = PARABOLA;
|
|
}
|
|
else {
|
|
conic = LINE;
|
|
}
|
|
}
|
|
|
|
if (GetEdgeDataInit(i)) {
|
|
/* */
|
|
/* initialize the parameterization formula for this Voronoi edge. */
|
|
/* */
|
|
if (conic == LINE) CreateLineData(i);
|
|
else if (conic == PARABOLA) CreateParabolaData(i);
|
|
else if (conic == HYPERBOLA) CreateHyperbolaData(i);
|
|
#ifdef GENUINE_ARCS
|
|
else if (conic == HYPERELL) CreateHyperbolaEllipseData(i);
|
|
#else
|
|
else if (conic == HYPERELL)
|
|
throw std::runtime_error("VRONI error: EvaluateBisectorData() - genuine arcs not supported!");
|
|
#endif
|
|
SetEdgeDataInit(i, false);
|
|
}
|
|
|
|
/* */
|
|
/* compute the offset point. */
|
|
/* */
|
|
if (conic == LINE) {
|
|
EvaluateLineData(i, t, w);
|
|
}
|
|
else if (conic == PARABOLA) {
|
|
if (t_lft == SEG)
|
|
EvaluateParabolaData(i, lft, t, w);
|
|
else
|
|
EvaluateParabolaData(i, rgt, t, w);
|
|
}
|
|
else if (conic == HYPERBOLA) {
|
|
EvaluateHyperbolaData(i, t, w);
|
|
}
|
|
#ifdef GENUINE_ARCS
|
|
else if (conic == HYPERELL) {
|
|
EvaluateHyperbolaEllipseData(i, t, w);
|
|
}
|
|
#else
|
|
else if (conic == HYPERELL)
|
|
throw std::runtime_error("VRONI error: EvaluateBisectorData() - genuine arcs not supported!");
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::ComputeOffset(int i, double_arg t)
|
|
{
|
|
int i0, i2, n1, n2, n;
|
|
int lft, rgt;
|
|
t_site t_lft, t_rgt, ti;
|
|
coord u, w;
|
|
double t1, t2;
|
|
vr_bool keep_going;
|
|
int i1;
|
|
|
|
NewOffsetCurve;
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffStart;
|
|
|
|
i0 = i;
|
|
n1 = GetStartNode(i);
|
|
n2 = GetEndNode(i);
|
|
t1 = GetNodeParam(n1);
|
|
t2 = GetNodeParam(n2);
|
|
|
|
//printf("\tComputeOffset i=%d, i0=%d, t=%e\n", i, i0, t);
|
|
|
|
do {
|
|
GetLftSiteData(i, &lft, &t_lft);
|
|
GetRgtSiteData(i, &rgt, &t_rgt);
|
|
|
|
EvaluateBisectorData(i, t, &w);
|
|
|
|
SetEdgeFlagNew(i, false);
|
|
|
|
/* */
|
|
/* store the computed point. make sure to include the reference to any */
|
|
/* original arc if arcs were approximated. */
|
|
/* */
|
|
if (t < t2) {
|
|
i2 = rgt;
|
|
ti = t_rgt;
|
|
i1 = NIL;
|
|
n = n2;
|
|
}
|
|
else {
|
|
assert(t < t1);
|
|
i2 = lft;
|
|
ti = t_lft;
|
|
i1 = NIL;
|
|
n = n1;
|
|
}
|
|
|
|
if (i1 == NIL) i1 = i2;
|
|
else ti = ARC;
|
|
|
|
if (ti == ARC) {
|
|
/* */
|
|
/* test whether point is inside or outside of arc. */
|
|
/* */
|
|
if (PntPntDist(GetArcCenter(i1), w) < GetArcRadius(i1))
|
|
ti = CCW;
|
|
else
|
|
ti = CW;
|
|
}
|
|
|
|
StoreOffsetData(i1, ti, w);
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffSegment;
|
|
|
|
/* */
|
|
/* find the next Voronoi edge */
|
|
/* */
|
|
keep_going = true;
|
|
do {
|
|
i = GetCCWEdge(i, n);
|
|
n1 = GetStartNode(i);
|
|
n2 = GetEndNode(i);
|
|
t1 = GetNodeParam(n1);
|
|
t2 = GetNodeParam(n2);
|
|
if ((t >= VroniMin(t1, t2)) && (t < VroniMax(t1, t2))) {
|
|
keep_going = false;
|
|
}
|
|
else {
|
|
n = GetOtherNode(i, n);
|
|
}
|
|
} while(keep_going);
|
|
|
|
} while (i != i0);
|
|
|
|
SetOffsetNumber;
|
|
|
|
/* */
|
|
/* let the user call some application-specific function */
|
|
/* */
|
|
ExtApplFuncOffsetEnd;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type vroniObject::GetExtApplOffsetFunc(int O)
|
|
{
|
|
assert(InOffsetData(O));
|
|
return offset_data[O].ext_appl;
|
|
}
|
|
|
|
|
|
void vroniObject::SetExtApplOffsetFunc(int O, eao_type ext_appl)
|
|
{
|
|
assert(InOffsetData(O));
|
|
offset_data[O].ext_appl = ext_appl;
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
void vroniObject::StartOffsets(void *output, int num_offset_loops)
|
|
{
|
|
/* */
|
|
/* write an approximate bounding box. note that this data need not be */
|
|
/* accurate, as we output the bbox of the input sites rather than the */
|
|
/* true bbox. */
|
|
/* */
|
|
/*
|
|
FP_fprintf((FILE*)output, "%f %f %f %f\n",
|
|
FP_PRNTARG(UnscaleX(GetPntCoords(0).x)), FP_PRNTARG(UnscaleY(GetPntCoords(0).y)),
|
|
FP_PRNTARG(UnscaleX(GetPntCoords(num_pnts-1).x)), FP_PRNTARG(UnscaleY(GetPntCoords(num_pnts-1).y)));
|
|
*/
|
|
|
|
/* */
|
|
/* write the number of individual closed offset loops */
|
|
/* */
|
|
fprintf((FILE*)output, "%d\n", num_offset_loops);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::StartOffsetLoop(void *output, int num_offset_elements)
|
|
{
|
|
fprintf((FILE*)output, "\n%d\n1\n", num_offset_elements);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetSeg(void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2)
|
|
{
|
|
FP_fprintf((FILE*)output, "0\n%20.16f %20.16f %f %f\n", FP_PRNTARG(x1), FP_PRNTARG(y1), FP_PRNTARG(x2 - x1), FP_PRNTARG(y2 - y1));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetArc(void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg , double_arg ,
|
|
double_arg xc, double_arg yc, vr_bool ccw)
|
|
{
|
|
if (ccw)
|
|
FP_fprintf((FILE*)output, "1\n%20.16f %20.16f %20.16f %20.16f\n", FP_PRNTARG(x1), FP_PRNTARG(y1), FP_PRNTARG(xc), FP_PRNTARG(yc));
|
|
else
|
|
FP_fprintf((FILE*)output, "-1\n%20.16f %20.16f %20.16f %20.16f\n", FP_PRNTARG(x1), FP_PRNTARG(y1), FP_PRNTARG(xc), FP_PRNTARG(yc));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsetLoop(void *, int, int, double_arg, double_arg)
|
|
{
|
|
/* dummy: no parameter names should avoid annoying compiler warnings about unused variables */
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsets(void *, int , int , int )
|
|
{
|
|
/* dummy: no parameter names should avoid annoying compiler warnings about unused variables */
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
/* this is the generic routine for exporting VRONI's offset data. it is used */
|
|
/* by VRONI to write offset data to .dat and .dxf files, and to add the */
|
|
/* offset data to the graphics buffer for display in the GUI. */
|
|
/* */
|
|
/* */
|
|
void vroniObject::apiOutputOffsets(void *output,
|
|
/* this is a pointer to a */
|
|
/* user-defined object, such as */
|
|
/* a file or a structure */
|
|
void (vroniObject::*StartOffsetsFuncPtr)(
|
|
void *output,
|
|
int num_offset_loops),
|
|
void (vroniObject::*StartOffsetLoopFuncPtr)(
|
|
void *output,
|
|
int num_offset_elements),
|
|
void (vroniObject::*WriteOffsetSegFuncPtr)(
|
|
void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2),
|
|
void (vroniObject::*WriteOffsetArcFuncPtr)(
|
|
void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2,
|
|
double_arg xc, double_arg yc,
|
|
vr_bool ccw),
|
|
void (vroniObject::*EndOffsetLoopFuncPtr)(
|
|
void *output,
|
|
int num_offset_segs,
|
|
int num_offset_arcs,
|
|
double_arg x0, double_arg y0),
|
|
void (vroniObject::*EndOffsetsFuncPtr)(
|
|
void *output,
|
|
int num_offset_loops,
|
|
int num_offset_segs,
|
|
int num_offset_arcs))
|
|
{
|
|
int i, j, k, m;
|
|
vr_bool ori;
|
|
int num_offset_segs = 0, num_offset_arcs = 0;
|
|
coord start, end, center;
|
|
|
|
/* */
|
|
/* start the offset curves */
|
|
/* */
|
|
(this->*StartOffsetsFuncPtr)(output, num_offset_list);
|
|
|
|
for (i = 0; i < num_offset_list; ++i) {
|
|
/* */
|
|
/* output one closed offset loop; the offset distance of this loop is */
|
|
/* stored in offset_list[i].offset. note that the orientation of a */
|
|
/* loop is dummy since there is no simple way for the code to */
|
|
/* determine the proper orientation of offset loops for general data. */
|
|
/* */
|
|
(this->*StartOffsetLoopFuncPtr)(
|
|
output, GetOffsetListEnd(i) - GetOffsetListStart(i) + 1
|
|
);
|
|
|
|
for (j = GetOffsetListStart(i); j <= GetOffsetListEnd(i); ++j) {
|
|
assert(InOffsetData(j));
|
|
start = GetOffsetPntCoords(j);
|
|
m = j + 1;
|
|
if (m > GetOffsetListEnd(i)) m = GetOffsetListStart(i);
|
|
end = GetOffsetPntCoords(m);
|
|
|
|
if (GetOffsetEleType(j) == PNT) {
|
|
/* */
|
|
/* CW circular arc centered at reflex vertex */
|
|
/* */
|
|
k = GetOffsetEleSite(j);
|
|
assert(InPntsList(k));
|
|
center = GetPntCoords(k);
|
|
ori = false;
|
|
|
|
if (ScrutinizeArc(start, end, center, &ori, ZERO_IO,
|
|
HasIncidentSite(k))) {
|
|
/* */
|
|
/* output an arc */
|
|
/* */
|
|
if (unscaleXYZ) {
|
|
(this->*WriteOffsetArcFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
UnscaleX(start.x), UnscaleY(start.y),
|
|
UnscaleX(end.x), UnscaleY(end.y),
|
|
UnscaleX(center.x), UnscaleY(center.y), ori
|
|
);
|
|
}
|
|
else {
|
|
(this->*WriteOffsetArcFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
start.x, start.y, end.x, end.y,
|
|
center.x, center.y, ori
|
|
);
|
|
}
|
|
++num_offset_arcs;
|
|
}
|
|
else {
|
|
/* */
|
|
/* replace arc by (tiny) line segment */
|
|
/* */
|
|
if (unscaleXYZ) {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
UnscaleX(start.x), UnscaleY(start.y),
|
|
UnscaleX(end.x), UnscaleY(end.y)
|
|
);
|
|
}
|
|
else {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
start.x, start.y, end.x, end.y
|
|
);
|
|
}
|
|
++num_offset_segs;
|
|
}
|
|
}
|
|
else if (GetOffsetEleType(j) == SEG) {
|
|
/* */
|
|
/* offset element is straight-line segment */
|
|
/* */
|
|
if (unscaleXYZ) {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
UnscaleX(start.x), UnscaleY(start.y),
|
|
UnscaleX(end.x), UnscaleY(end.y)
|
|
);
|
|
}
|
|
else {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
start.x, start.y, end.x, end.y
|
|
);
|
|
}
|
|
++num_offset_segs;
|
|
}
|
|
else if ((GetOffsetEleType(j) == CCW) ||
|
|
(GetOffsetEleType(j) == CW)) {
|
|
/* */
|
|
/* offset element is circular arc defined by an input arc */
|
|
/* */
|
|
k = GetOffsetEleSite(j);
|
|
assert(InArcsList(k));
|
|
center = GetArcCenter(k);
|
|
if (GetOffsetEleType(j) == CCW) ori = true;
|
|
else ori = false;
|
|
if (ScrutinizeArc(start, end, center, &ori, ZERO_IO, true)) {
|
|
/* */
|
|
/* output an arc */
|
|
/* */
|
|
if (unscaleXYZ) {
|
|
(this->*WriteOffsetArcFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
UnscaleX(start.x), UnscaleY(start.y),
|
|
UnscaleX(end.x), UnscaleY(end.y),
|
|
UnscaleX(center.x), UnscaleY(center.y), ori
|
|
);
|
|
}
|
|
else {
|
|
(this->*WriteOffsetArcFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
start.x, start.y, end.x, end.y,
|
|
center.x, center.y, ori
|
|
);
|
|
}
|
|
++num_offset_arcs;
|
|
}
|
|
else {
|
|
/* */
|
|
/* replace arc by (tiny) line segment */
|
|
/* */
|
|
if (unscaleXYZ) {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
UnscaleX(start.x), UnscaleY(start.y),
|
|
UnscaleX(end.x), UnscaleY(end.y)
|
|
);
|
|
}
|
|
else {
|
|
(this->*WriteOffsetSegFuncPtr)(
|
|
output,
|
|
#ifdef EXT_APPL_OFF
|
|
GetExtApplOffsetFunc(j),
|
|
#endif
|
|
start.x, start.y, end.x, end.y
|
|
);
|
|
}
|
|
++num_offset_segs;
|
|
}
|
|
}
|
|
else {
|
|
/* */
|
|
/* not exactly an offset type that the code should have assigned */
|
|
/* */
|
|
VD_Warning("apiOutputOffsets() - unknown site type!\n");
|
|
assert(0 == 1);
|
|
}
|
|
}
|
|
j = GetOffsetListStart(i);
|
|
if (unscaleXYZ) {
|
|
(this->*EndOffsetLoopFuncPtr)(output, num_offset_segs, num_offset_arcs,
|
|
UnscaleX(GetOffsetXCoord(j)),
|
|
UnscaleY(GetOffsetYCoord(j)));
|
|
}
|
|
else {
|
|
(this->*EndOffsetLoopFuncPtr)(output, num_offset_segs, num_offset_arcs,
|
|
GetOffsetXCoord(j), GetOffsetYCoord(j));
|
|
}
|
|
}
|
|
(this->*EndOffsetsFuncPtr)(
|
|
output, num_offset_list, num_offset_segs, num_offset_arcs
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsets(char off_file[])
|
|
{
|
|
FILE *off;
|
|
|
|
assert(gt(scale_factor, ZERO));
|
|
|
|
unscaleXYZ = true;
|
|
|
|
off = OpenFileVD(off_file, "w");
|
|
|
|
apiOutputOffsets(off, &vroniObject::StartOffsets,
|
|
&vroniObject::StartOffsetLoop,
|
|
&vroniObject::WriteOffsetSeg, &vroniObject::WriteOffsetArc,
|
|
&vroniObject::EndOffsetLoop, &vroniObject::EndOffsets);
|
|
|
|
fclose(off);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* toggle between LWPOLYLINE and POLYLINE */
|
|
static vr_bool LW_polyline = false;
|
|
|
|
void vroniObject::StartOffsetsDXF(void *output, int )
|
|
{
|
|
StartDXFFile((FILE*)output);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::StartOffsetLoopDXF(void *output, int )
|
|
{
|
|
if (LW_polyline) WriteDXFLWPoly_Start((FILE*)output, true, DXF_RED);
|
|
else WriteDXFPoly_Start((FILE*)output, true, DXF_RED);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetSegDXF(void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg , double_arg )
|
|
{
|
|
if (LW_polyline)
|
|
WriteDXFLWPoly_Vertex((FILE*)output,
|
|
x1, y1, 0.0);
|
|
else
|
|
WriteDXFPoly_Vertex((FILE*)output,
|
|
x1, y1, 0.0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetArcDXF(void *output,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2,
|
|
double_arg xc, double_arg yc, vr_bool ccw)
|
|
{
|
|
double radius, bulge;
|
|
coord start, end, center;
|
|
|
|
start.x = x1;
|
|
start.y = y1;
|
|
end.x = x2;
|
|
end.y = y2;
|
|
center.x = xc;
|
|
center.y = yc;
|
|
radius = PntPntDist(start, center);
|
|
|
|
if (ccw)
|
|
ComputeBulge(start, end, &bulge, center, radius, ARC);
|
|
else
|
|
ComputeBulge(start, end, &bulge, center, radius, -ARC);
|
|
|
|
if (LW_polyline)
|
|
WriteDXFLWPoly_Vertex((FILE*)output, start.x, start.y, bulge);
|
|
else
|
|
WriteDXFPoly_Vertex((FILE*)output, start.x, start.y, bulge);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsetLoopDXF(void *output, int, int, double_arg,
|
|
double_arg)
|
|
{
|
|
if (!LW_polyline) WriteDXFPoly_End((FILE*)output);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsetsDXF(void *output, int , int , int )
|
|
{
|
|
FinishDXFFile((FILE*)output);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetsDXF(char off_file[])
|
|
{
|
|
FILE *off;
|
|
|
|
assert(gt(scale_factor, ZERO));
|
|
|
|
unscaleXYZ = true;
|
|
|
|
off = OpenFileVD(off_file, "w");
|
|
|
|
apiOutputOffsets(off, &vroniObject::StartOffsetsDXF,
|
|
&vroniObject::StartOffsetLoopDXF,
|
|
&vroniObject::WriteOffsetSegDXF,
|
|
&vroniObject::WriteOffsetArcDXF,
|
|
&vroniObject::EndOffsetLoopDXF,
|
|
&vroniObject::EndOffsetsDXF);
|
|
|
|
fclose(off);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef GRAPHICS
|
|
|
|
void vroniObject::StartOffsetsBuf(void* , int )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::StartOffsetLoopBuf(void* , int )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetSegBuf(void* ,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2)
|
|
{
|
|
AddOffToBuffer(x1, y1, x2, y2, 0.0, 0.0, 0.0, false, OffColor);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void vroniObject::WriteOffsetArcBuf(void* ,
|
|
#ifdef EXT_APPL_OFF
|
|
eao_type ext_appl_off,
|
|
#endif
|
|
double_arg x1, double_arg y1,
|
|
double_arg x2, double_arg y2,
|
|
double_arg xc, double_arg yc, vr_bool ccw)
|
|
{
|
|
double radius;
|
|
|
|
radius = sqrt(pow(x1-xc,2) + pow(y1-yc,2));
|
|
|
|
AddOffToBuffer(x1, y1, x2, y2, xc, yc, radius, !ccw, OffColor);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsetLoopBuf(void* , int , int , double_arg , double_arg)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::EndOffsetsBuf(void*, int , int , int )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void vroniObject::AddOffsetsToBuffer(void)
|
|
{
|
|
int *foo = (int*)NIL; /* dummy pointer, we access VRONI's global data */
|
|
|
|
unscaleXYZ = false;
|
|
|
|
apiOutputOffsets(foo, &vroniObject::StartOffsetsBuf,
|
|
&vroniObject::StartOffsetLoopBuf,
|
|
&vroniObject::WriteOffsetSegBuf,
|
|
&vroniObject::WriteOffsetArcBuf,
|
|
&vroniObject::EndOffsetLoopBuf,
|
|
&vroniObject::EndOffsetsBuf);
|
|
|
|
unscaleXYZ = true;
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#include "ext_appl_offset.cc"
|
|
|
|
|
|
|