Files
vroni/prepro.cc
T
SaraP 739088af9f Vroni 7.8 :
- aggiornamento versione.
2025-01-29 16:24:30 +01:00

832 lines
29 KiB
C++

/*****************************************************************************/
/* */
/* Copyright (C) 2002--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-172 */
/* Voice Mail: (+43 662) 8044-6304 */
/* Snail Mail: Martin Held */
/* FB Informatik */
/* Universitaet Salzburg */
/* A-5020 Salzburg, Austria */
/* */
/*****************************************************************************/
/* */
/* get standard libraries */
/* */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
/* */
/* get my header files */
/* */
#include "fpkernel.h"
#include "vronivector.h"
#include "vroni_object.h"
#include "defs.h"
#include "numerics.h"
void vroniObject::SetPreprocessingThreshold(double_arg threshold)
{
if (!ThresholdReached()) preProZero = threshold * 10.0;
else preProZero = ZERO_IO;
return;
}
/* */
/* computes the line equations for all segments; very short segments are */
/* discarded and their endpoints are merged. */
/* */
void vroniObject::PreprocessSegments(int *num_critical_segs)
{
int i, i1, i2;
coord p, q, vec;
double dist, c, d, foo;
int number = 0;
vr_bool remove_segments = false;
for (i = 0; i < num_segs; ++i) {
p = GetSegStartCoord(i);
q = GetSegEndCoord(i);
vec = VecSub(q, p);
dist = VecLen(vec);
/* */
/* check whether the segment length is less than VRONI's threshold. if */
/* a segment is too short then we merge its endpoints and (lateron) */
/* delete that segment. care is taken that all other segments incident */
/* upon one of those two endpoints are updated accordingly. */
/* */
if (le(dist, preProZero)) {
if ((p.x != q.x) || (p.y != q.y)) {
if (GetDataSorted()) {
VD_IO_Warning("PreprocessSegments() - line segment too short!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(p.x), UnscaleY(p.y),
UnscaleX(q.x), UnscaleY(q.y));
#endif
i1 = Get1stVtx(i, SEG);
i2 = Get2ndVtx(i, SEG);
MergePoints(i1, i2);
if (i1 > i2) {
RepairPntLinks(i1, i2, true);
SetDeletedPnt(i1, true);
}
else {
RepairPntLinks(i2, i1, true);
SetDeletedPnt(i2, true);
}
restart = true;
}
else {
/* */
/* we need to identify and eliminate duplicate vertices prior */
/* to merging any pair of vertices. */
/* */
VD_IO_Warning("PreprocessSegments() - line segment too short!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(p.x), UnscaleY(p.y),
UnscaleX(q.x), UnscaleY(q.y));
#endif
restart = true;
return;
}
}
else {
/* */
/* this is a zero-length segment. it is deleted later in another */
/* call to CleanData(). */
/* */
VD_IO_Warning("PreprocessSegments() - zero-length segment!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(p.x), UnscaleY(p.y),
UnscaleX(q.x), UnscaleY(q.y));
#endif
remove_segments = true;
}
}
else {
/* */
/* this is a normal segment. we compute the (Hessian) equation of */
/* of its supporting line. */
/* */
if (lt(dist, preProZero)) ++number;
vec = VecDiv(dist, vec);
c = vec.y * p.x - vec.x * p.y;
SetSegEqnData(i, -vec.y, vec.x, c);
foo = PntLineDist(-vec.y, vec.x, c, q);
if (!(eq(foo, ZERO))) {
/* */
/* the other endpoint does not satisfy that line's equation, for */
/* reasons unknown. we adjust the constant as good as this is */
/* possible on a floating-point arithmetic... */
/* */
VD_Warning("PreprocessSegments() - problematic seg equation!");
numerical = true;
d = vec.y * q.x - vec.x * q.y;
c = (c + d) / 2.0;
SetSegEqnData(i, -vec.y, vec.x, c);
}
SetSegLgth(i, dist);
}
}
if (remove_segments) restart = true;
*num_critical_segs = number;
return;
}
/* */
/* break up arcs that are larger than a quarter-circle and adjust the center */
/* of an arc if its radii differ. */
/* */
void vroniObject::AdjustArcs(int *num_critical_arcs)
{
double orient, dist, diff, radius, radius1, radius2, delta;
coord s, e, c, vec, m, nor, start, end;
int i, i1, i2;
int number;
double secdist;
#ifndef NDEBUG
int s_orient;
#endif
#ifdef EXT_APPL_SITES
eas_type ext_appl;
#endif
vr_bool remove_arcs = false;
*num_critical_arcs = 0;
for (i = 0; i < num_arcs; ++i) {
s = GetArcStartCoord(i);
e = GetArcEndCoord(i);
vec = VecSub(s, e);
dist = VecLen(vec);
if (le(dist, preProZero)) {
if ((s.x != e.x) || (s.y != e.y)) {
if (GetDataSorted()) {
VD_IO_Warning("AdjustArcs() - chord length too short!");
#ifdef VRONI_IO_WARN
c = GetArcCenter(i);
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y),
UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
i1 = Get1stVtx(i, ARC);
i2 = Get2ndVtx(i, ARC);
MergePoints(i1, i2);
if (i1 > i2) {
RepairPntLinks(i1, i2, true);
SetDeletedPnt(i1, true);
}
else {
RepairPntLinks(i2, i1, true);
SetDeletedPnt(i2, true);
}
restart = true;
}
else {
/* */
/* we need to identify and eliminate duplicate vertices prior */
/* to merging any pair of vertices. */
/* */
VD_IO_Warning("AdjustArcs() - chord length too short!");
#ifdef VRONI_IO_WARN
c = GetArcCenter(i);
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y),
UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
restart = true;
return;
}
}
else {
/* */
/* this is a zero-length arc. it is deleted later in another */
/* call to CleanData(). */
/* */
remove_arcs = true;
}
}
}
if (remove_arcs) restart = true;
if (restart) return;
number = 0;
i = 0;
while (i < num_arcs) {
s = GetArcStartCoord(i);
e = GetArcEndCoord(i);
c = GetArcCenter(i);
orient = VecDet(c, s, e);
if (orient < 0.0) {
vec = VecSub(s, e);
}
else {
vec = VecSub(e, s);
}
#ifndef NDEBUG
s_orient = SignEps(orient, ZERO);
#endif
dist = VecLen(vec);
delta = dist / 2.0;
#ifdef EXT_APPL_SITES
ext_appl = GetExtApplArc(i);
#endif
/* */
/* compute "the" radius. */
/* */
radius1 = PntPntDist(s, c);
radius2 = PntPntDist(e, c);
radius = (radius1 + radius2) / 2.0;
start = VecSub(s, c);
end = VecSub(e, c);
secdist = radius*radius - PntPntDistSq(s,e)/4.0;
if (secdist <= 0.0) secdist = radius;
else secdist = radius - sqrt(secdist);
if (radius <= preProZero) {
/* */
/* this arc has a tiny radius! */
/* */
VD_IO_Warning("AdjustArcs() - radius too small!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y), UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
numerical = true;
i1 = Get1stVtx(i, ARC);
i2 = Get2ndVtx(i, ARC);
#ifdef EXT_APPL_SITES
if (GetArcOrientation(i)) StoreSeg(i1, i2, ext_appl);
else StoreSeg(i2, i1, ext_appl);
#else
if (GetArcOrientation(i)) StoreSeg(i1, i2);
else StoreSeg(i2, i1);
#endif
RemoveArc(i);
}
else if(le(secdist, preProZero) || le(secdist/radius, preProZero)) {
/* */
/* The maximum radial distance between arc and its secant is less */
/* less than eps. */
/* */
VD_IO_Warning("AdjustArcs() - arc is almost segment!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y), UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
numerical = true;
i1 = Get1stVtx(i, ARC);
i2 = Get2ndVtx(i, ARC);
#ifdef EXT_APPL_SITES
if (GetArcOrientation(i)) StoreSeg(i1, i2, ext_appl);
else StoreSeg(i2, i1, ext_appl);
#else
if (GetArcOrientation(i)) StoreSeg(i1, i2);
else StoreSeg(i2, i1);
#endif
RemoveArc(i);
}
else {
diff = (radius1 - radius2);
if (ne(diff, ZERO)) {
/* */
/* the radii differ! we'll adjust the center... */
/* */
VD_IO_Warning("AdjustArcs() - center of arc adjusted!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y), UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
m = MidPoint(s, e);
nor = VecCCW(vec);
diff = radius * radius - delta * delta;
if (diff <= 0.0) diff = 0.0;
else diff = sqrt(diff) / dist;
nor = VecMult(diff, nor);
m = VecAdd(m, nor);
orient = VecDet(m, s, e);
#ifndef NDEBUG
assert((SignEps(orient, ZERO) == 0) || (s_orient == 0) ||
(SignEps(orient, ZERO) == s_orient));
#endif
radius1 = PntPntDist(s, m);
radius2 = PntPntDist(e, m);
radius = (radius1 + radius2) / 2.0;
assert(eq(radius1 - radius2, ZERO_MAX));
SetArcCenter(i, m);
++number;
}
else {
radius = (radius1 + radius2) / 2.0;
}
assert(radius > preProZero);
SetArcRadius(i, radius);
//printf("radius %30.16f\n", radius);
++i;
}
}
*num_critical_arcs = number;
if (remove_arcs) restart = true;
return;
}
/* */
/* arcs that are close to (or greater than) a semi-circle are broken */
/* up into pieces that are clearly smaller than a semi-circle. also, */
/* "the" radius of the circular arc is computed, and, if necessary, a */
/* new center is computed. */
/* */
/* note that we store all input arcs as CCW arcs! */
/* */
void vroniObject::PreprocessArcs(int *num_critical_arcs)
{
double orient, radius, a, b;
double dist, cosine;
coord s, e, c, cs, ce, vec, split, norm;
int i, i2;
#ifdef TRACE
vr_bool trace_prep = false;
#endif
/* */
/* make sure that all circular arcs have consistent radii. if necessary, */
/* we'll adjust the centers!! */
/* */
AdjustArcs(num_critical_arcs);
if (restart) return;
for (i = 0; i < num_arcs; ++i) {
s = GetArcStartCoord(i);
e = GetArcEndCoord(i);
c = GetArcCenter(i);
radius = GetArcRadius(i);
assert(radius > preProZero);
orient = VecDet(c, s, e);
//Get CW oriented p1-p2 vector.
if (orient < 0.0) {
vec = VecSub(s, e);
}
else {
vec = VecSub(e, s);
}
//Secant length and and arms-vectors
dist = VecLen(vec);
cs = VecSub(s, c);
ce = VecSub(e, c);
//Cosine of the angle of arc
cosine = VecDotProd(cs, ce) / (radius * radius);
//printf("arc%d c=(%e,%e), s=(%e,%e), e=(%e,%e)\n", i, c.x, c.y, s.x, s.y, e.x, e.y);
//printf("\tcs=(%e,%e), ce=(%e,%e), radius=%e\n", cs.x, cs.y, ce.x, ce.y, radius);
//printf("\tcosine: %e, orient: %e\n", cosine, orient);
//Almost a full circle --> split circle to quarter-circle
if ((Abs(1.0-cosine) <= SMALL) && (orient < 0.0)) {
VD_IO_Warning("PreprocessArcs() - arc equals full circle!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y),
UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
norm = VecCCW(cs);
split = VecSub(c, norm);
#ifdef EXT_APPL_PNTS
i2 = StorePnt(split.x, split.y, eap_NIL);
#else
i2 = StorePnt(split.x, split.y);
#endif
(void) SplitArc(i, i2);
split = VecSub(c, cs);
#ifdef EXT_APPL_PNTS
i2 = StorePnt(split.x, split.y, eap_NIL);
#else
i2 = StorePnt(split.x, split.y);
#endif
(void) SplitArc(i, i2);
split = VecAdd(c, norm);
#ifdef EXT_APPL_PNTS
i2 = StorePnt(split.x, split.y, eap_NIL);
#else
i2 = StorePnt(split.x, split.y);
#endif
(void) SplitArc(i, i2);
restart = true;
}
//Larger than quarter circle, smaller than semi-circle --> split it
//else if( cosine<0 && orient>=0 )
else if((Abs(cosine+1.0) <= SMALL) && (orient >= 0.0)) {
VD_IO_Warning("PreprocessArcs() - (almost) semi-circle!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y), UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
split = VecCCW(vec);
split = VecMult( -radius/dist, split);
split = VecAdd(c, split);
//printf("\tsplit at (%e,%e)\n", split.x, split.y);
#ifdef EXT_APPL_PNTS
i2 = StorePnt(split.x, split.y, eap_NIL);
#else
i2 = StorePnt(split.x, split.y);
#endif
(void) SplitArc(i, i2);
restart = true;
}
//Larger than semi-circle --> put it into four pieces
else if (orient < 0.0 ) {
VD_IO_Warning("PreprocessArcs() - larger than semi-circle!");
#ifdef VRONI_IO_WARN
printf("%20.16f %20.16f %20.16f %20.16f %20.16f %20.16f\n",
UnscaleX(s.x), UnscaleY(s.y),
UnscaleX(e.x), UnscaleY(e.y),
UnscaleX(c.x), UnscaleY(c.y));
#endif
split = VecCCW(vec);
split = VecMult( radius/dist, split);
split = VecAdd(c, split);
//printf("\tsplit at (%e,%e)\n", split.x, split.y);
#ifdef EXT_APPL_PNTS
i2 = StorePnt(split.x, split.y, eap_NIL);
#else
i2 = StorePnt(split.x, split.y);
#endif
(void) SplitArc(i, i2);
restart = true;
}
/* */
/* set the unit normal vectors at the start point and the end */
/* point */
/* */
#ifdef VRONI_WARN
if (!eq(VecLen(cs)-radius, ZERO_MAX)) {
printf("arc %d\n", i);
printf("distance1 = %20.16f, zero_max = %20.16f\n",
VecLen(cs)-radius, ZERO_MAX);
printf("distance2 = %20.16f, zero_max = %20.16f\n",
VecLen(ce)-radius, ZERO_MAX);
}
if (!eq(VecLen(ce)-radius, ZERO_MAX)) {
printf("arc %d\n", i);
printf("distance1 = %20.16f, zero_max = %20.16f\n",
VecLen(cs)-radius, ZERO_MAX);
printf("distance2 = %20.16f, zero_max = %20.16f\n",
VecLen(ce)-radius, ZERO_MAX);
}
#endif
assert( eq(VecLen(cs)-radius, ZERO_MAX));
assert( eq(VecLen(ce)-radius, ZERO_MAX));
cs = VecDiv(radius, cs);
ce = VecDiv(radius, ce);
#ifdef TRACE
if (trace_prep) {
if (!eq(VecLen(cs) - 1.0, ZERO) ||
!eq(VecLen(ce) - 1.0, ZERO)) {
printf("radius: %24.20f\n", radius);
printf("arc %d: VecLen(cs) = %24.20f\n",
i, VecLen(cs));
printf("arc %d: VecLen(ce) = %24.20f\n",
i, VecLen(ce));
}
}
#endif
SetArcStartNormal(i, cs);
SetArcEndNormal(i, ce);
/* */
/* compute the line equation of the chord */
/* */
if (gt(dist,ZERO)) {
vec = VecDiv(dist, vec);
a = vec.y * s.x - vec.x * s.y;
SetChordEqnData(i, -vec.y, vec.x, a);
b = PntLineDist(-vec.y, vec.x, a, e);
if (!(eq(b, ZERO))) {
VD_Warning("PreprocessArcs() - problematic chord equation");
numerical = true;
b = vec.y * e.x - vec.x * e.y;
a = (a + b) / 2.0;
SetChordEqnData(i, -vec.y, vec.x, a);
}
}
}
return;
}
void vroniObject::ExtendBoundingBox(void)
{
double radius, delta;
coord p1, p2, p3;
int i, k, k1, k2;
for (i = 0; i < num_arcs; ++i) {
p1 = GetArcStartCoord(i);
p2 = GetArcEndCoord(i);
p3 = GetArcCenter(i);
radius = GetArcRadius(i);
if (p1.x >= p3.x) {
if (p1.y >= p3.y) k1 = 0;
else k1 = 3;
}
else {
if (p1.y >= p3.y) k1 = 1;
else k1 = 2;
}
if (p2.x >= p3.x) {
if (p2.y >= p3.y) k2 = 0;
else k2 = 3;
}
else {
if (p2.y >= p3.y) k2 = 1;
else k2 = 2;
}
if (k2 < k1) {
k2 += 4;
}
else if (k1 == k2) {
if (k1 == 0) {
if ((p1.x < p2.x) && (p1.y > p2.y)) k2 = 4;
}
else if (k1 == 1) {
if ((p1.x < p2.x) && (p1.y < p2.y)) k2 = 5;
}
else if (k1 == 2) {
if ((p1.x > p2.x) && (p1.y < p2.y)) k2 = 6;
}
else if (k1 == 3) {
if ((p1.x > p2.x) && (p1.y > p2.y)) k2 = 7;
}
}
for (k = k1; k < k2; ++k) {
switch(k) {
case 0:
case 4:
delta = p3.y + radius;
if (delta > bb_max.y) {
bb_max.y = delta;
}
break;
case 1:
case 5:
delta = p3.x - radius;
if (delta < bb_min.x) {
bb_min.x = delta;
}
break;
case 2:
case 6:
delta = p3.y - radius;
if (delta < bb_min.y) {
bb_min.y = delta;
}
break;
case 3:
case 7:
delta = p3.x + radius;
if (delta > bb_max.x) {
bb_max.x = delta;
}
break;
default:
assert(0 == 1);
break;
}
}
}
bb_min.x -= ZERO_MAX;
bb_min.y -= ZERO_MAX;
bb_max.x += ZERO_MAX;
bb_max.y += ZERO_MAX;
return;
}
void vroniObject::HandleBulge(const coord & p1, /* start point of arc */
const coord & p2, /* end point of arc */
double *bulge, /* bulge factor */
coord *center, /* center of arc */
double *radius, /* radius of arc */
int *attr) /* SEG, ARC or -ARC */
{
coord v, q;
double s, d;
if (eq(*bulge, ZERO)) {
/* */
/* circular arc degenerates to line segment */
/* */
*bulge = 0.0;
*attr = SEG;
return;
}
if (Abs(*bulge) > 1.0) {
/* */
/* this arc spans more than 180 degrees! if beta == 90 - alpha then */
/* tan beta = cot alpha; i.e., tan beta = 1 / bulge, where beta is */
/* one quarter of the included angle of the complementary arc! we can */
/* proceed as normal after inverting the direction vector v from the */
/* start point to the end point of the arc. */
/* */
*bulge = 1.0 / *bulge;
v = VecSub(p1, p2);
}
else {
v = VecSub(p2, p1);
}
d = VecLen(v);
if (eq(d, ZERO)) {
/* */
/* chord length too small; we replace the arc by a line segment. note: */
/* this might mishandle arcs that are close to a full circle! */
/* */
*bulge = 0.0;
*attr = SEG;
return;
}
*radius = d *(1.0 + *bulge * *bulge) / (4.0 * *bulge); /* radius */
s = *radius - d * *bulge / 2.0; /* radial offset */
q = MidPoint(p1, p2);
center->x = q.x - s * v.y / d;
center->y = q.y + s * v.x / d;
if (*radius < 0.0) *radius = - *radius;
if (*bulge > 0) *attr = ARC;
else *attr = - ARC;
return;
}
void vroniObject::ComputeBulge(const coord & p1, const coord & p2,
double *bulge, const coord & center,
double_arg radius, int attr)
{
coord v, q, w;
double s, d, delta;
if ((attr != ARC) && (attr != -ARC)) {
/* */
/* not a circular arc! */
/* */
*bulge = 0.0;
}
else {
v = VecSub(p2, p1);
d = VecLen(v);
if (eq(d, ZERO)) {
/* */
/* chord length too small; we replace arc by a line segment */
/* */
*bulge = 0.0;
}
else {
q = MidPoint(p1, p2);
w = VecSub(center, q);
s = VecLen(w);
*bulge = (radius - s) * 2.0 / d;
if (attr == ARC) {
delta = - w.x * v.y + w.y * v.x;
}
else {
delta = w.x * v.y - w.y * v.x;
*bulge = - *bulge;
}
/* */
/* check whether the arc spans more than 180 degrees! */
/* */
if (delta < 0.0) {
if (!eq(*bulge, ZERO)) {
*bulge = 1.0 / *bulge;
}
else {
/* */
/* this seems to be a gigantic arc that is close to a full */
/* circle, and bulge should be +/- infinity... we'll use a */
/* value that won't cause headaches in any subsequent */
/* processing. */
/* */
if (*bulge < 0.0) *bulge = (double) INT_MAX;
else *bulge = (double) INT_MIN;
}
}
}
}
return;
}
void vroniObject::ReplaceArc(int i)
{
int i1, i2;
i1 = Get1stVtx(i, ARC);
i2 = Get2ndVtx(i, ARC);
#ifdef EXT_APPL_SITES
if (GetArcOrientation(i)) StoreSeg(i1, i2, GetExtApplArc(i));
else StoreSeg(i2, i1, GetExtApplArc(i));
#else
if (GetArcOrientation(i)) StoreSeg(i1, i2);
else StoreSeg(i2, i1);
#endif
RemoveArc(i);
restart_due_to_intersection = true;
numerical = true;
return;
}
void vroniObject::ReplaceSeg(int i)
{
int i1, i2;
i1 = Get1stVtx(i, SEG);
i2 = Get2ndVtx(i, SEG);
MergePoints(i1, i2);
if (i1 > i2) {
RepairPntLinks(i1, i2, true);
SetDeletedPnt(i1, true);
}
else {
RepairPntLinks(i2, i1, true);
SetDeletedPnt(i2, true);
}
restart_due_to_intersection = true;
numerical = true;
return;
}