// written by g.j.hawkesford 2006 for Camtek Gmbh // // This program is released under the BSD license. See the file COPYING for details. // #include "geometry.h" using namespace geoff_geometry; #ifdef PEPSPOST #include "postoutput.h" #endif //////////////////////////////////////////////////////////////////////////////////////////////// // kurve //////////////////////////////////////////////////////////////////////////////////////////////// namespace geoff_geometry { SpanVertex::SpanVertex() { for(int i = 0; i < SPANSTORAGE; i++) index[i] = NULL; } SpanVertex::~SpanVertex() { #ifndef PEPSDLL // don't know what peps did about this? for(int i = 0; i < SPANSTORAGE; i++) { if(index[i] != NULL) { delete index[i]; } } #endif } const SpanVertex& SpanVertex::operator= (const SpanVertex& spv ){ /// if (this == &spv) return *this; memcpy(x, spv.x, SPANSTORAGE * sizeof(double)); memcpy(y, spv.y, SPANSTORAGE * sizeof(double)); memcpy(xc, spv.xc, SPANSTORAGE * sizeof(double)); memcpy(yc, spv.yc, SPANSTORAGE * sizeof(double)); for(unsigned int i = 0; i < SPANSTORAGE; i++) { type[i] = spv.type[i]; spanid[i] = spv.spanid[i]; index[i] = spv.index[i]; #ifndef PEPSDLL if(index[i] != NULL) { SpanDataObject* obj = new SpanDataObject(index[i]); index[i] = obj; } #endif } return *this; } void SpanVertex::Add(int offset, int spantype, const Point& p, const Point& pc, int ID) { type[offset] = spantype; // index[offset] = NULL; x[offset] = p.x; y[offset] = p.y; xc[offset] = pc.x; yc[offset] = pc.y; spanid[offset] = ID; } void SpanVertex::AddSpanID(int offset, int ID) { spanid[offset] = ID; } #if PEPSDLL void SpanVertex::Add(int offset, WireExtraData* Index ) { index[offset] = Index; } WireExtraData* SpanVertex::Get(int offset) { return index[offset]; } #else void SpanVertex::Add(int offset, const SpanDataObject* Index ) { index[offset] = Index; } const SpanDataObject* SpanVertex::GetIndex(int offset) const{ return index[offset]; } #endif int SpanVertex::Get(int offset, Point& pe, Point& pc) { pe = Point(x[offset], y[offset]); pc = Point(xc[offset], yc[offset]); return type[offset]; } int SpanVertex::GetSpanID(int offset) { return spanid[offset]; } Span Span::Offset(double offset) { Span Offsp = *this; if(FNEZ(offset) && !NullSpan) { if ( !dir ) { // straight Offsp.p0.x -= offset * vs.gety(); Offsp.p0.y += offset * vs.getx(); Offsp.p1.x -= offset * vs.gety(); Offsp.p1.y += offset * vs.getx(); } else { // circular span // double coffset = (double) dir * offset; Offsp.p0.x -= vs.gety() * offset; Offsp.p0.y += vs.getx() * offset; Offsp.p1.x -= ve.gety() * offset; Offsp.p1.y += ve.getx() * offset; // Offsp.radius -= dir * offset; } Offsp.SetProperties(true); } return Offsp; } bool Span::JoinSeparateSpans(Span& sp) { // this method joins this span to sp where they are separated normally by an offset from original // // parameters:- // Input sp near span // Output this->p1 and sp.p0 assigned to the spans intersection Point inters; int turnLeft = ((this->ve ^ sp.vs) > 0)? 1 : -1; if(!this->dir) { CLine one(*this); if(!sp.dir) { // line line CLine two(sp); inters = one.Intof(two); } else { // line arc Circle two(sp); inters = one.Intof(-turnLeft * sp.dir, two); } } else { Circle one(*this); if(!sp.dir) { // arc line CLine two(sp); inters = two.Intof(turnLeft * this->dir, one); } else { // arc arc Circle two(sp); inters = one.Intof(-turnLeft * this->dir * sp.dir, two); } } if(inters.ok) { this->p1 = sp.p0 = inters; this->SetProperties(true); sp.SetProperties(true); } return inters.ok; } static int Split(double tolerance, double angle, double radius, int dir); int Span::Split(double tolerance) { // returns the number of divisions required to keep in tolerance if(!returnSpanProperties) this->SetProperties(true); return geoff_geometry::Split(tolerance, angle, radius, dir); } #if 0 int Span3d::Split(double tolerance) { // returns the number of divisions required to keep in tolerance if(!returnSpanProperties) this->SetProperties(true); return geoff_geometry::Split(tolerance, angle, radius, dir); } #endif static int Split(double tolerance, double angle, double radius, int dir) { if(dir == LINEAR) // straight span return 0; double cosa = 1 - tolerance / radius; if(cosa > NEARLY_ONE) cosa = NEARLY_ONE; cosa = 2 * cosa * cosa - 1 ; /* double angle */ double sina = sqrt(1 - cosa * cosa) * dir; double tempang = atan2(sina, cosa); int num_vectors = (int)(fabs (angle / tempang )) + 1 ; return num_vectors; } void Span::SplitMatrix(int num_vectors, Matrix* matrix) { // returns the incremental matrix matrix->Unit(); if(dir) { // arc span double incang = angle / (double) num_vectors ; matrix->Translate(-pc.x, -pc.y, 0); matrix->Rotate(incang, 3); matrix->Translate(pc.x, pc.y, 0); } else { // linear span matrix->Translate(length / num_vectors * vs.getx(), length / num_vectors * vs.gety(), 0); } } #if 0 void Span3d::SplitMatrix(int num_vectors, Matrix* matrix) { // returns the incremental matrix matrix->Unit(); if(dir) { // arc span if(normal.getz() <= NEARLY_ONE) FAILURE(getMessage(L"Unfinished coding - contact the Company", GENERAL_MESSAGES, MES_UNFINISHEDCODING)); double incang = angle / (double) num_vectors ; matrix->Translate(-pc.x, -pc.y, -pc.z); matrix->Rotate(incang, 3); matrix->Translate(pc.x, pc.y, pc.z); } else { // linear span double d = length / num_vectors; matrix->Translate(d * vs.getx(), d * vs.gety(), d * vs.getz()); } } #endif void Span::minmax(Box& box, bool start) { minmax(box.min, box.max, start); } #if 0 void Span3d::minmax(Box3d& box, bool start) { minmax(box.min, box.max, start); } #endif void Span::minmax(Point& min, Point& max, bool start) { // box a span (min/max) if(start) { MinMax(p0, min, max); } MinMax(p1, min, max); if(dir) { // check the quadrant points double dx1 = p1.x - p0.x; double dy1 = p1.y - p0.y; double dx = pc.x - p0.x; double dy = pc.y - p0.y; double dx0 = dx + radius; // 0deg if( dir * (dx0 * dy1 - dx1 * dy) > 0) { if(pc.x + radius > max.x) max.x = pc.x + radius; } dx0 = dx - radius; // 180deg if( dir * (dx0 * dy1 - dx1 * dy) > 0) { if(pc.x - radius < min.x) min.x = pc.x - radius; } double dy0 = dy + radius; // 90deg if( dir * (dx * dy1 - dx1 * dy0) > 0) { if(pc.y + radius > max.y) max.y = pc.y + radius; } dy0 = dy - radius; // 270deg if( dir * (dx * dy1 - dx1 * dy0) > 0) { if(pc.y - radius < min.y) min.y = pc.y - radius; } } } #if 0 void Span3d::minmax(Point3d& min, Point3d& max, bool start) { // box a span (min/max) if(start) { MinMax(p0, min, max); } MinMax(p1, min, max); if(dir) { // check the quadrant points ... MUST RECODE THIS FOR 3D sometime double dx1 = p1.x - p0.x; double dy1 = p1.y - p0.y; double dx = pc.x - p0.x; double dy = pc.y - p0.y; double dx0 = dx + radius; // 0deg if( dir * (dx0 * dy1 - dx1 * dy) > 0) { if(pc.x + radius > max.x) max.x = pc.x + radius; } dx0 = dx - radius; // 180deg if( dir * (dx0 * dy1 - dx1 * dy) > 0) { if(pc.x - radius < min.x) min.x = pc.x - radius; } double dy0 = dy + radius; // 90deg if( dir * (dx * dy1 - dx1 * dy0) > 0) { if(pc.y + radius > max.y) max.y = pc.y + radius; } dy0 = dy - radius; // 270deg if( dir * (dx * dy1 - dx1 * dy0) > 0) { if(pc.y - radius < min.y) min.y = pc.y - radius; } } } #endif int Span::Intof(const Span& sp, Point& pInt1, Point& pInt2, double t[4])const { // Intof 2 spans return geoff_geometry::Intof(*this, sp, pInt1, pInt2, t); } Point Span::Near(const Point& p)const{ // returns the near point to span from p if(this->dir == LINEAR) { double t; t = (Vector2d(this->p0, p) * this->vs); // t parametrised 0 - line length return this->vs * t + this->p0; } else { double r = p.Dist(this->pc); if(r < geoff_geometry::TOLERANCE) return (p.Dist(this->p0) < p.Dist(this->p1))?this->p0 : this->p1; return(p.Mid(this->pc, (r - this->radius) / r)); } } Point Span::NearOn(const Point& p)const{ // returns the near point to span from p - returned point is always on the span Point pn; pn = Near(p); if(this->OnSpan(pn)) return pn; // return nearest endpoint return (pn.Dist(p0) < pn.Dist(p1))?p0 : p1; } void Span::Transform(const Matrix& m, bool setprops) { p0 = p0.Transform(m); p1 = p1.Transform(m); if(dir != LINEAR) { pc = pc.Transform(m); if(m.m_mirrored == -1) FAILURE(L"Don't know mirror - use IsMirrored method on object"); if(m.m_mirrored) dir = -dir; } if(setprops) SetProperties(true); } #if 0 void Span3d::Transform(const Matrix& m, bool setprops) { p0 = p0.Transform(m); p1 = p1.Transform(m); if(dir != LINEAR) { pc = pc.Transform(m); normal.Transform(m); if(m.m_mirrored == -1) FAILURE(L"Don't know mirror - use IsMirrored method on object"); if(m.m_mirrored) dir = -dir; } if(setprops) SetProperties(true); } #endif Point Span::Mid()const { // midpoint of a span return geoff_geometry::Mid(*this); } Point Span::MidPerim(double d)const { /// returns a point which is 0-d along span Point p; if(this->dir == LINEAR) { p = this->vs * d + this->p0; } else { Vector2d v(pc, p0); v.Rotate(d * dir / this->radius); p = v + pc; } return p; } Point Span::MidParam(double param)const { /// returns a point which is 0-1 along span if(fabs(param) < 0.00000000000001) return p0; if(fabs(param - 1.0) < 0.00000000000001) return p1; return MidPerim(param * this->length); } Vector2d Span::GetVector(double fraction)const { /// returns the direction vector at point which is 0-1 along span if(dir == 0){ Vector2d v(p0, p1); v.normalise(); return v; } Point p= MidParam(fraction); Vector2d v(pc, p); v.normalise(); if(dir == ACW) { return Vector2d(-v.gety(), v.getx()); } else { return Vector2d(v.gety(), -v.getx()); } } Kurve::Kurve(const Kurve& k) :Matrix(){ /// copy constructor this->m_nVertices = k.m_nVertices; memcpy(this->e, k.e, 16 * sizeof(double)); this->m_unit = k.m_unit; this->m_mirrored = k.m_mirrored; this->m_isReversed = k.m_isReversed; this->m_started = k.m_started; for(unsigned int i = 0; i < k.m_spans.size(); i++) { SpanVertex* spv = new SpanVertex; *spv = *k.m_spans[i]; this->m_spans.push_back(spv); } } const Kurve& Kurve::operator=( const Kurve &k) { if (this == &k) return *this; memcpy(e, k.e, 16 * sizeof(double)); m_unit = k.m_unit; m_mirrored = k.m_mirrored; m_isReversed = k.m_isReversed; this->Clear(); if(k.m_nVertices) m_started = true; // m_nVertices = 0; // spVertex spv; // for(int i = 0; i < k.m_nVertices; i++) { // k.Get(i, spv); // Add(spv); // } for(unsigned int i = 0; i < k.m_spans.size(); i++) { SpanVertex* spv = new SpanVertex; *spv = *k.m_spans[i]; this->m_spans.push_back(spv); } m_nVertices = k.m_nVertices; return *this; } #if 0 Kurve::Kurve(Kurve& k) :Matrix(){ *this = k; return; *this = Matrix(k); Point p, pc; m_nVertices = 0; for(int i = 0; i < k.m_nVertices; i++) { int spantype = k.Get(i, p, pc); int spanid = k.GetSpanID(i); if(Add(spantype, p, pc)) this->AddSpanID(spanid); } if(k.m_nVertices) m_started = true; } const Kurve& Kurve::operator=( Kurve &k) { *this = Matrix(k); Point p, pc; this->Clear(); m_isReversed = k.m_isReversed; for(int i = 0; i < k.m_nVertices; i++) { int spantype = k.Get(i, p, pc); int spanid = k.GetSpanID(i); if(Add(spantype, p, pc)) this->AddSpanID(spanid); } if(k.m_nVertices) m_started = true; return *this; } #endif const Kurve& Kurve::operator=(const Matrix &m) { // *this = Matrix(m); // return *this; for(int i = 0; i < 16; i++) e[i] = m.e[i]; m_unit = m.m_unit; m_mirrored = m.m_mirrored; return *this; } Kurve::~Kurve() { this->Clear(); } bool Kurve::Closed()const { // returns true if kurve closed if(m_nVertices > 1) { Point ps, pe, pc; Get(0, ps, pc); Get(m_nVertices - 1, pe, pc); return (ps == pe); } else return false; } void Kurve::FullCircle(int dir, const Point& c, double radius) { /// make a full circle Kurve (2 spans) /// mark the first span for later this->Clear(); Point ps = c; ps.x = c.x + radius; this->Start(ps); this->AddSpanID(FULL_CIRCLE_KURVE); ps.x = c.x - radius; this->Add(dir, ps, c, true); ps.x = c.x + radius; this->Add(dir, ps, c, true); } void Kurve::Start() { if(m_started) this->Clear(); m_started = true; } void Kurve::Start(const Point& p) { Start(); Add(0, p, Point(0,0)); } bool Kurve::Add(const Span& sp, bool AddNullSpans) { // add a span, including ID if(!this->m_started) this->Start(sp.p0); if(this->Add(sp.dir, sp.p1, sp.pc, AddNullSpans)) { this->AddSpanID(sp.ID); return true; } return false; } bool Kurve::Add(const spVertex& spv, bool AddNullSpans) { if(Add(spv.type, spv.p, spv.pc, AddNullSpans)) { AddSpanID(spv.spanid); return true; } return false; } bool Kurve::Add(int span_type, const Point& p0, const Point& pc, bool AddNullSpans) { // add a span (cw = -1 (T) acw = 1 (A) ) #ifdef _DEBUG //if(this == NULL) FAILURE(L"Kurve::Add - No Kurve Object"); #endif if(!m_started) { Start(p0); return true; } if(m_nVertices) { // see if a null span would result by the addition of this span // double xl, yl, cxl, cyl; Point pv, pcc; Get(m_nVertices - 1, pv, pcc); if(pv.Dist(p0) < geoff_geometry::TOLERANCE) { if(!AddNullSpans) return false; span_type = LINEAR; // linear span } } SpanVertex* p; if(m_nVertices % SPANSTORAGE == 0) { p = new SpanVertex; m_spans.push_back(p); } else p = (SpanVertex*) m_spans[m_nVertices / SPANSTORAGE]; p->Add(m_nVertices % SPANSTORAGE, span_type, p0, pc); m_nVertices++; return true; } void Kurve::AddSpanID(int ID) { // add a extra data - must be called after Add int vertex = this->m_nVertices - 1; SpanVertex* p = (SpanVertex*) m_spans[vertex / SPANSTORAGE]; p->AddSpanID(vertex % SPANSTORAGE, ID); } void Kurve::Add() { // null span if(m_nVertices == 0) FAILURE(L"Invalid attempt to add null span - no start"); Point p, pc; Get(m_nVertices - 1, p, pc); Add(p, true); } bool Kurve::Add(const Point& p0, bool AddNullSpans) { return Add(0, p0, Point(0,0), AddNullSpans); } void Kurve::Add(const Kurve* k, bool AddNullSpans) { Span sp; Matrix m; if(!this->m_unit) { m = *k; Matrix im = this->Inverse(); m.Multiply(im); m.IsUnit(); } for(int i = 1; i <= k->nSpans(); i++) { k->Get(i, sp, false, this->m_unit); #ifndef PEPSDLL const SpanDataObject* obj = k->GetIndex(i-1); #endif if(!this->m_unit) sp.Transform(m); if(i == 1) { // check if this is the same as last point in kurve bool AddFirstVertex = true; if(nSpans()) { Span spLast; Get(nSpans(), spLast, false, false); if(spLast.p1.Dist(sp.p0) <= geoff_geometry::TOLERANCE) AddFirstVertex = false; } if(AddFirstVertex) { Add(sp.p0, AddNullSpans); #ifndef PEPSDLL if(obj != NULL) { SpanDataObject* objnew = new SpanDataObject(obj); AddIndex(nSpans() - 1, objnew); } #endif } } Add(sp.dir, sp.p1, sp.pc, AddNullSpans); #ifndef PEPSDLL if(obj != NULL) { SpanDataObject* objnew = new SpanDataObject(obj); AddIndex(nSpans() - 1, objnew); } #endif } } void Kurve::Replace(int vertexnumber, const spVertex& spv) { // replace a span Replace(vertexnumber, spv.type, spv.p, spv.pc, spv.spanid); } void Kurve::Replace(int vertexnumber, int type, const Point& p0, const Point& pc, int ID) { // replace a span #ifdef _DEBUG if(vertexnumber > m_nVertices) FAILURE(getMessage(L"Kurve::Replace - vertexNumber out of range")); #endif SpanVertex* p = (SpanVertex*) m_spans[vertexnumber / SPANSTORAGE]; p->Add(vertexnumber % SPANSTORAGE, type, p0, pc, ID); } #ifdef PEPSDLL void Kurve::ModifyIndex(int vertexnumber, WireExtraData* i) { // replace an index #ifdef _DEBUG if(vertexnumber > m_nVertices) FAILURE(getMessage(L"Kurve::ModifyIndex - vertexNumber out of range")); #endif SpanVertex* p = (SpanVertex*) m_spans[vertexnumber / SPANSTORAGE]; p->Add(vertexnumber % SPANSTORAGE, i); } #else void Kurve::AddIndex(int vertexNumber, const SpanDataObject* data) { if(vertexNumber > m_nVertices - 1) FAILURE(L"Kurve::AddIndex - vertexNumber out of range"); SpanVertex* p = (SpanVertex*) m_spans[vertexNumber / SPANSTORAGE]; p->Add(vertexNumber % SPANSTORAGE, data); } const SpanDataObject* Kurve::GetIndex(int vertexNumber)const { if(vertexNumber > m_nVertices - 1) FAILURE(L"Kurve::GetIndex - vertexNumber out of range"); SpanVertex* p = (SpanVertex*) m_spans[vertexNumber / SPANSTORAGE]; return p->GetIndex(vertexNumber % SPANSTORAGE); } #endif void Kurve::Get(std::vector *all, bool igNoreNullSpans)const { /// put all spans to vector for(int i = 1; i <= nSpans(); i++) { Span sp; Get(i, sp, true); if(igNoreNullSpans && sp.NullSpan) continue; all->push_back(sp); } } void Kurve::Get(int vertexnumber, spVertex& spv) const { spv.type = Get(vertexnumber, spv.p, spv.pc); spv.spanid = GetSpanID(vertexnumber); } int Kurve::Get(int vertexnumber, Point& pe, Point& pc) const { // returns spantype with end / centre by reference if(vertexnumber < 0 || vertexnumber >= m_nVertices) FAILURE(getMessage(L"Kurve::Get - vertexNumber out of range")); if(m_isReversed) { int revVertexnumber = m_nVertices - 1 - vertexnumber; SpanVertex* p = (SpanVertex*)m_spans[revVertexnumber / SPANSTORAGE]; int offset = revVertexnumber % SPANSTORAGE; pe = Point(p->x[offset], p->y[offset]); if(vertexnumber > 0) { revVertexnumber++; offset = revVertexnumber % SPANSTORAGE; p = (SpanVertex*)m_spans[revVertexnumber / SPANSTORAGE]; pc = Point(p->xc[offset], p->yc[offset]); return -p->type[offset]; } else return LINEAR; } else { SpanVertex* p = (SpanVertex*)m_spans[vertexnumber / SPANSTORAGE]; return p->Get(vertexnumber % SPANSTORAGE, pe, pc); } } int Kurve::GetSpanID(int vertexnumber) const { // for spanID (wire offset) if(vertexnumber < 0 || vertexnumber >= m_nVertices) FAILURE(getMessage(L"Kurve::Get - vertexNumber out of range")); if(m_isReversed) vertexnumber = m_nVertices - 1 - vertexnumber; SpanVertex* p = (SpanVertex*)m_spans[vertexnumber / SPANSTORAGE]; return p->GetSpanID(vertexnumber % SPANSTORAGE); } int Kurve::Get(int spannumber, Span& sp, bool returnSpanProperties, bool transform) const { // returns span data and optional properties - the function returns as the span type if(spannumber < 1 || spannumber > m_nVertices) FAILURE(getMessage(L"Kurve::Get - vertexNumber out of range")); if(m_nVertices < 2) return -99; int spanVertexNumber = spannumber - 1; if(m_isReversed) spanVertexNumber = m_nVertices - 1 - spanVertexNumber; SpanVertex* p = (SpanVertex*)m_spans[spanVertexNumber / SPANSTORAGE]; sp.p0.x = p->x[spanVertexNumber % SPANSTORAGE]; sp.p0.y = p->y[spanVertexNumber % SPANSTORAGE]; sp.p0.ok = 1; sp.dir = Get(spannumber, sp.p1, sp.pc); sp.ID = GetSpanID(spannumber); if(transform && !m_unit) { const Matrix *m = this; sp.Transform(*m, false); } sp.SetProperties(returnSpanProperties); return sp.dir; } #if 0 int Kurve::Get(int spannumber, Span3d& sp, bool returnSpanProperties, bool transform) const { // returns span data and optional properties - the function returns as the span type if(spannumber < 1 || spannumber > m_nVertices) FAILURE(getMessage(L"Kurve::Get - vertexNumber out of range")); if(m_nVertices < 2) return -99; int spanVertexNumber = spannumber - 1; SpanVertex* p = (SpanVertex*)m_spans[spanVertexNumber / SPANSTORAGE]; sp.p0.x = p->x[spanVertexNumber % SPANSTORAGE]; sp.p0.y = p->y[spanVertexNumber % SPANSTORAGE]; sp.p0.z = 0; // sp.p0.ok = 1; sp.dir = Get(spannumber, sp.p1, sp.pc); if(transform && !m_unit) { const Matrix *m = this; sp.Transform(*m, false); } sp.SetProperties(returnSpanProperties); return sp.dir; } #endif void Kurve::Get(Point &ps,Point &pe) const { // returns the start- and endpoint of the kurve Span sp; Get(1,sp,true,true); ps = sp.p0; Get(m_nVertices-1,sp,true,true); pe = sp.p1; } void Span::SetProperties(bool returnProperties) { returnSpanProperties = returnProperties; if(returnSpanProperties) { // return span properties if(dir) { // arc properties vs = ~Vector2d(pc, p0); // tangent at start ( perp to radial vector) ve = ~Vector2d(pc, p1); // tangent at end ( perp to radial vector) if(dir == CW) { vs = -vs; // reverse directions for CW arc ve = -ve; } radius = vs.normalise(); double radCheck = ve.normalise(); // if(FNE(radius, radCheck, geoff_geometry::TOLERANCE * 0.5)){ if(FNE(radius, radCheck, geoff_geometry::TOLERANCE)){ FAILURE(getMessage(L"Invalid Geometry - Radii mismatch - SetProperties")); } length = 0.0; angle = 0.0; if(radius > geoff_geometry::TOLERANCE) { if((NullSpan = (p0.Dist(p1)) <= geoff_geometry::TOLERANCE)) { dir = LINEAR; } else { // arc length & included angle length = fabs(angle = IncludedAngle(vs, ve, dir)) * radius; } } else NullSpan = true; } else { // straight properties vs = Vector2d(p0, p1); length = vs.normalise(); NullSpan = (length <= geoff_geometry::TOLERANCE); ve = vs; } minmax(box, true); } } #if 0 void Span3d::SetProperties(bool returnProperties) { if(returnSpanProperties = returnProperties) { // return span properties if(dir) { // arc properties vs = normal ^ Vector3d(pc, p0);// tangent at start ( perp to radial vector) ve = normal ^ Vector3d(pc, p1);// tangent at end ( perp to radial vector) if(dir == CW) { vs = -vs; // reverse directions for CW arc ve = -ve; } radius = vs.normalise(); double radCheck = ve.normalise(); if(FNE(radius, radCheck, geoff_geometry::TOLERANCE * 0.5)) FAILURE(getMessage(L"Invalid Geometry - Radii mismatch - SetProperties", GEOMETRY_ERROR_MESSAGES, MES_INVALIDARC)); if(radius > geoff_geometry::TOLERANCE) { if(NullSpan = (p0.Dist(p1) <= geoff_geometry::TOLERANCE)) { length = 0.0; angle = 0.0; dir = LINEAR; } else { // arc length & included angle length = fabs(angle = IncludedAngle(vs, ve, normal, dir)) * radius; } } else NullSpan = true; } else { // straight properties vs = Vector3d(p0, p1); length = vs.normalise(); NullSpan = (length <= geoff_geometry::TOLERANCE); ve = vs; } minmax(box, true); } } #endif Point Mid(const Span& span) { // mid point of a span if(span.dir) { CLine chord(span.p0, span.p1); if(chord.ok) { CLine bisector(Mid(span.p0, span.p1), ~chord.v, false); return Intof((span.dir == CW) ?FARINT : NEARINT, bisector, Circle(span)); } else return span.p0; } else return Mid(span.p0, span.p1); } Point Kurve::Near(const Point& p, int& nearSpanNumber)const { // finds the nearest span on kurve to the given point, nearSpanNumber is the spannumber double minDist = 1.0e100; Point pNear, pn; nearSpanNumber = 0; for(int i = 1; i <= nSpans(); i++) { Span sp; Get(i, sp, true, true); pNear = sp.NearOn(p); double d = pNear.Dist(p); if(minDist > d) { nearSpanNumber = i; pn = pNear; minDist = d; if(minDist < geoff_geometry::TOLERANCE) break; // p must be on the span } } return pn; } Point Kurve::NearToVertex(const Point& p, int& nearSpanNumber)const { // finds the nearest span endpoint on kurve to the given point, nearSpanNumber is the spannumber double minDistSquared = 1.0e100; Point pn; Matrix inv_mat = *this; inv_mat.Inverse(); Point tp = p; if(!m_unit) tp = tp.Transform(inv_mat); // Inverse transform point (rather than transform each vertex!) nearSpanNumber = 0; for(int i = 0; i < m_nVertices; i++) { Point ps, pc; Get(i, ps, pc); double DistSquared = Vector2d(ps, tp).magnitudesqd(); if(DistSquared < minDistSquared) { minDistSquared = DistSquared; nearSpanNumber = i; pn = ps; } } return pn.Transform(*this); } void Kurve::ChangeStart(const Point *pNewStart, int startSpanno) { // changes the start position of the Kurve if(startSpanno == 1) { Span spFirst; this->Get(1, spFirst, false, true); if(spFirst.p0 == *pNewStart) return; } else if(startSpanno == this->nSpans()) { Span spLast; this->Get(this->nSpans(), spLast, false, true); if(spLast.p1 == *pNewStart) return; } Kurve temp; bool wrapped = false; int spanno = startSpanno; Span sp; for(int nSpans = 0; nSpans <= this->nSpans(); nSpans++) { this->Get(spanno, sp, false, true); if(spanno == startSpanno && !wrapped) { temp.Start(*pNewStart); temp.Add(sp.dir, sp.p1, sp.pc, true); } else { if(nSpans == this->nSpans() && this->Closed()) { sp.p1 = *pNewStart; } temp.Add(sp, true); } spanno++; if(spanno > this->nSpans()) { if(!this->Closed()) break; spanno = 1; wrapped = true; } } *this = temp; } void Kurve::ChangeEnd(const Point *pNewEnd, int endSpanno) { // changes the end position of the Kurve, doesn't keep closed kurves closed if(endSpanno == 1) { Span spFirst; this->Get(1, spFirst, false, true); if(spFirst.p0 == *pNewEnd) return; } else if(endSpanno == this->nSpans()) { Span spLast; this->Get(this->nSpans(), spLast, false, true); if(spLast.p1 == *pNewEnd) return; } Kurve temp; Span sp; for(int spanno = 1; spanno != (endSpanno + 1); spanno++) { this->Get(spanno, sp, false, true); if(spanno == 1) { temp.Start(sp.p0); } if(spanno == endSpanno)sp.p1 = *pNewEnd; temp.Add(sp.dir, sp.p1, sp.pc, true); if(spanno == endSpanno)break; //spanno++; } *this = temp; } void Kurve::minmax(Point& min, Point& max) { // boxes kurve double xscale = 1.0; min = Point(1.0e61, 1.0e61); max = Point(-1.0e61, -1.0e61); if(!GetScale(xscale)) FAILURE(getMessage(L"Differential Scale not allowed for this method")); // differential scale Span sp; for(int i = 1; i < m_nVertices; i++) { Get(i, sp, true, true); if(i == 1) MinMax(sp.p0, min, max); sp.minmax(min, max, false); } } void Kurve::minmax(Box& b) { minmax(b.min, b.max); } void Kurve::StoreAllSpans(std::vector& kSpans)const { // store all kurve spans in array, normally when fast access is reqd Span span; for(int i = 1; i <= this->nSpans(); i++) { this->Get(i, span, true, false); kSpans.push_back(span); } } void Kurve::Clear() { for(vector::iterator It = m_spans.begin(); It != m_spans.end(); It++) { SpanVertex* spv = *It; delete spv; } m_spans.clear(); m_started = false; m_nVertices = 0; m_isReversed = false; } bool Kurve::operator==(const Kurve &k)const{ // k = kk (vertex check) if(nSpans() != k.nSpans()) return false; spVertex thisvertex, vertex; for(int i = 0; i <= nSpans(); i++) { this->Get(i, thisvertex); k.Get(i, vertex); if(thisvertex != vertex) return false; } return true; } double Kurve::Perim() const{ // returns perimeter of kurve double perim = 0; Span sp; double xscale = 1.0; if(!GetScale(xscale)) FAILURE(getMessage(L"Differential Scale not allowed for this method")); // differential scale if(m_nVertices > 1) { for(int i = 1; i < m_nVertices; i++) perim += (Get(i, sp, true))? fabs(sp.angle) * sp.radius : sp.length; } return perim * xscale; } double Kurve::Area() const{ // returns Area of kurve (+ve clockwise , -ve anti-clockwise sense) double xscale = 1.0; double area = 0; Span sp; if(Closed()) { if(!GetScale(xscale)) FAILURE(getMessage(L"Differential Scale not allowed for this method")); // differential scale for(int i = 1; i < m_nVertices; i++) { if(Get(i, sp, true)) area += ( 0.5 * ((sp.pc.x - sp.p0.x) * (sp.pc.y + sp.p0.y) - (sp.pc.x - sp.p1.x) * (sp.pc.y + sp.p1.y) - sp.angle * sp.radius * sp.radius)); else area += 0.5 * (sp.p1.x - sp.p0.x) * (sp.p0.y + sp.p1.y); } } return area * xscale * xscale; } static void bubblesort(vector&p, vector& d); int Kurve::Intof(const Span& spin, vector& p)const { // returns a vector (array) of intersection points int totalPoints = 0; for(int i = 1; i <= nSpans(); i++) { Span sp; Get(i, sp, true, true); Point pInt1, pInt2; double t[4]; int numint = sp.Intof(spin, pInt1, pInt2, t); if(numint) p.push_back(pInt1); if(numint == 2) p.push_back(pInt2); totalPoints += numint; } if(totalPoints) { // sort intersects along span vector d; Span temp(spin); for(int i = 0; i < (int)p.size(); i++) { temp.p1 = p[i]; temp.SetProperties(true); d.push_back(temp.length); } bubblesort(p, d); } return totalPoints; } static void bubblesort(vector&p, vector& d) { for(int pass = 1; pass < (int)p.size() ; pass++) { for(int j = 0; j < (int)p.size() - 1; j++) { if(d[j] > d[j+1] ) { // swap Point temp = p[j]; p[j] = p[j+1]; p[j+1] = temp; double dtemp = d[j]; d[j] = d[j+1]; d[j+1] = dtemp; } } } } int Kurve::Intof(const Kurve&k, vector& p)const { vector all; int totalPoints = 0; for(int i = 1; i <= nSpans(); i++) { Span sp; Get(i, sp, true, true); vector p0; totalPoints += k.Intof(sp, p0); for(int j = 0; j < (int)p0.size(); j++) all.push_back(p0[j]); } (void)totalPoints; //FILE* d; //d = fopen("\\temp\\test.txt", "w"); // for(int l = 0; l < all.size(); l++) all[l].print(d, "all","\n"); for(int i = 0; i < (int)all.size(); i++) { if(i == 0) p.push_back(all[0]); else if(all[i-1].Dist(all[i]) > geoff_geometry::TOLERANCE) p.push_back(all[i]); } //fclose(d); return (int)p.size(); } bool Kurve::Split(double MaximumRadius, double resolution) { Span sp; bool changed = false; Kurve ko; Get(0, sp.p0, sp.pc); ko.Start(sp.p0); for(int i = 1 ; i < m_nVertices; i++) { sp.dir = Get(i, sp.p1, sp.pc); if(sp.dir) { sp.SetProperties(true); if(sp.radius >= MaximumRadius) { // split this arc int nSplits = sp.Split(resolution); if(nSplits > 1) { Matrix m; sp.SplitMatrix(nSplits, &m); for(int j = 1; j < nSplits; j++) { sp.p0 = sp.p0.Transform(m); ko.Add(sp.p0); } sp.dir = LINEAR; changed = true; } } } ko.Add(sp.dir, sp.p1, sp.pc); sp.p0 = sp.p1; } // copy kurve if(changed) *this = ko; return changed; } void Kurve::Reverse() { // reverse the direction of a kurve int nSwaps = (m_nVertices - 1) / 2; if(nSwaps == 0) return; Point p0, pc0; // near Point pend, pcend; // far int i = 0, j = m_nVertices - 1; int dir0 = Get(i, p0, pc0); int spanID0 = GetSpanID(i); int dirend = Get(j, pend, pcend); int spanIDend = GetSpanID(j); while(i <= nSwaps) { Point p1, pc1; int dir1 = Get(i+1, p1, pc1); int spanID1 = GetSpanID(i+1); Point pendp, pcendp; // far previous int direndp = Get(j-1, pendp, pcendp); int spanIDendp = GetSpanID(j-1); // end point Replace(i, dir0, pend, pc0, spanID0); Replace(j, dirend, p0, pcend, spanIDend); dir0 = dir1; p0 = p1; pc0 = pc1; dirend = direndp; pend = pendp; pcend = pcendp; spanID0 = spanID1; spanIDend = spanIDendp; i++; j--; } // now circle data - but it should be easy to modify centre data in the loop above (for another day) i = 0; j = m_nVertices - 1; dir0 = Get(i, p0, pc0); dirend = Get(j, pend, pcend); while(i < nSwaps) { Point p1, pc1; Point pendp, pcendp; // far previous int dir1 = Get(i+1, p1, pc1); int direndp = Get(j-1, pendp, pcendp); Replace(i+1, -dirend, p1, pcend); Replace(j, -dir1, pend, pc1); dir0 = dir1; p0 = p1; pc0 = pc1; dirend = direndp; pend = pendp; pcend = pcendp; i++; j--; } } int Kurve::Reduce(double tolerance) { // remove spans that lie within tolerance // returns the number of spans removed if(nSpans() <= 2) // too few spans for this method return 0; Kurve kReduced; kReduced = Matrix(*this); #if 0 for(int i = 1; i <= this->nSpans(); i++) { Span sp; this->Get(i, sp, true); for(int j = i+1; j <= this->nSpans(); j++) { Span spnext; this->Get(j, spnext, true); } } return m_nVertices - kReduced.m_nVertices; #else int dir1, dir2 = 0; Point p0, p1, p2, pc0, pc1, pc2; int vertex = 0; int dir0 = Get(vertex++, p0, pc0); // first vertex kReduced.Start(p0); int lvertex = vertex++; while(vertex < m_nVertices) { while(vertex < m_nVertices) { int savelvertex = lvertex; int addvertex = vertex - 1; dir2 = Get(vertex++, p2, pc2); CLine cl(p0, p2); if(cl.ok) { bool outoftol = false; while(lvertex < vertex - 1) { // interior loop, p1 after p0 up to vertex before p2 dir1 = Get(lvertex++, p1, pc1); if(dir1 || fabs(cl.Dist(p1)) > tolerance) { outoftol = true; break; } } if(outoftol) { dir0 = Get(addvertex, p0, pc0); kReduced.Add(dir0, p0, pc0); lvertex = addvertex + 1; } else { lvertex = savelvertex; } } } } kReduced.Add(dir2, p2, pc2); if(m_nVertices != kReduced.m_nVertices) *this = kReduced; return m_nVertices - kReduced.m_nVertices; #endif } void Kurve::Part(int startVertex, int EndVertex, Kurve *part) { // make a part kurve spVertex spv; for(int i = startVertex; i <= EndVertex; i++) { Get(i, spv); part->Add(spv, true); } return; } Kurve Kurve::Part(int fromSpanno, const Point& fromPt, int toSpanno, const Point& toPt) { // make a Part Kurve // if spanno are known containing from/to Points then this is used, otherwise set = 0 Kurve kPart; Span span; Point ps,pe; int iStartSpanNr,iEndSpanNr,i; // get start point and start spannumber if(fromSpanno == 0) ps = Near(fromPt,iStartSpanNr); else { Get(fromSpanno,span,true,true); ps = span.p0; iStartSpanNr = fromSpanno; } // get end point and end spannumber if(toSpanno == 0) pe = Near(toPt,iEndSpanNr); else { Get(toSpanno,span,true,true); pe = span.p1; iEndSpanNr = toSpanno; } kPart.Start(ps); Get(iStartSpanNr,span,true,true); if(iStartSpanNr == iEndSpanNr) { kPart.Add(span.dir,pe,span.pc); return kPart; } if(iStartSpanNr < iEndSpanNr) { for(i=iStartSpanNr;i iEndSpanNr) { for(i=iStartSpanNr;i<=nSpans();i++) { Get(i,span,true,true); kPart.Add(span.dir,span.p1,span.pc); } if(!Closed()) { Get(1,span,true,true); kPart.Add(0,span.p0,Point(0.0,0.0)); // Add new span from kend to kstart } for(i=1;iPerim(); double fromPerim = fromParam * perimTotal; double toPerim = toParam * perimTotal; double perim = 0.; double perimLast = 0.; for(int i = 1; i <= this->nSpans(); i++) { Span sp; this->Get(i, sp, true, true); perim += sp.length; if(fromPerim <= perim && !k.m_started) { // start if(FEQ(fromPerim, perim)) k.Start(sp.p0); else { double d = fromPerim - perimLast; k.Start(sp.MidPerim(d)); } } if(perim >= toPerim) { // end if(FEQ(toPerim, perim)) k.Add(sp); else { double d = toPerim - perimLast; sp.p1 = sp.MidPerim(d); k.Add(sp); } break; } if(k.m_started) k.Add(sp); perimLast = perim; } return k; } void tangential_arc(const Point &p0, const Point &p1, const Vector2d &v0, Point &c, int &dir) { // sets dir to 0, if a line is needed, else to 1 or -1 for acw or cw arc and sets c dir = 0; if(p0.Dist(p1) > 0.0000000001 && v0.magnitude() > 0.0000000001){ Vector2d v1(p0, p1); Point halfway(p0 + Point(v1 * 0.5)); Plane pl1(halfway, v1); Plane pl2(p0, v0); Line plane_line; if(pl1.Intof(pl2, plane_line)) { Line l1(halfway, v1); double t1, t2; Line lshort; plane_line.Shortest(l1, lshort, t1, t2); c = lshort.p0; Vector3d cross = Vector3d(v0) ^ Vector3d(v1); dir = (cross.getz() > 0) ? 1:-1; } } } }