| | |
| | |
| | |
| | |
| |
|
| | #include "Curve.h" |
| | #include "Circle.h" |
| | #include "Arc.h" |
| | #include "Area.h" |
| | #include "kurve/geometry.h" |
| |
|
| | const Point operator*(const double& d, const Point& p) |
| | { |
| | return p * d; |
| | } |
| | double Point::tolerance = 0.001; |
| |
|
| | |
| |
|
| | |
| | |
| | bool Point::operator==(const Point& p) const |
| | { |
| | return fabs(x - p.x) < tolerance && fabs(y - p.y) < tolerance; |
| | } |
| |
|
| | double Point::length() const |
| | { |
| | return sqrt(x * x + y * y); |
| | } |
| |
|
| | double Point::normalize() |
| | { |
| | double len = length(); |
| | if (fabs(len) > 0.000000000000001) { |
| | *this = (*this) / len; |
| | } |
| | return len; |
| | } |
| |
|
| | Line::Line(const Point& P0, const Point& V) |
| | : p0(P0) |
| | , v(V) |
| | {} |
| |
|
| | double Line::Dist(const Point& p) const |
| | { |
| | Point vn = v; |
| | vn.normalize(); |
| | double d1 = p0 * vn; |
| | double d2 = p * vn; |
| | Point pn = p0 + vn * (d2 - d1); |
| |
|
| | return pn.dist(p); |
| | } |
| |
|
| | CVertex::CVertex(int type, const Point& p, const Point& c, int user_data) |
| | : m_type(type) |
| | , m_p(p) |
| | , m_c(c) |
| | , m_user_data(user_data) |
| | {} |
| |
|
| | CVertex::CVertex(const Point& p, int user_data) |
| | : m_type(0) |
| | , m_p(p) |
| | , m_c(0.0, 0.0) |
| | , m_user_data(user_data) |
| | {} |
| |
|
| | void CCurve::append(const CVertex& vertex) |
| | { |
| | m_vertices.push_back(vertex); |
| | } |
| |
|
| | bool CCurve::CheckForArc( |
| | const CVertex& prev_vt, |
| | std::list<const CVertex*>& might_be_an_arc, |
| | CArc& arc_returned |
| | ) |
| | { |
| | |
| | |
| | |
| | if (might_be_an_arc.size() < 2) { |
| | return false; |
| | } |
| |
|
| | |
| | std::size_t num = might_be_an_arc.size(); |
| | std::size_t i = 0; |
| | const CVertex* mid_vt = NULL; |
| | std::size_t mid_i = (num - 1) / 2; |
| | for (std::list<const CVertex*>::iterator It = might_be_an_arc.begin(); |
| | It != might_be_an_arc.end(); |
| | It++, i++) { |
| | if (i == mid_i) { |
| | mid_vt = *It; |
| | break; |
| | } |
| | } |
| |
|
| | if (mid_vt == NULL) { |
| | return false; |
| | } |
| |
|
| | |
| | Point p0(prev_vt.m_p); |
| | Point p1(mid_vt->m_p); |
| | Point p2(might_be_an_arc.back()->m_p); |
| | Circle c(p0, p1, p2); |
| |
|
| | const CVertex* current_vt = &prev_vt; |
| | |
| | |
| | |
| | |
| | double accuracy = CArea::m_accuracy * 2.3 / CArea::m_units; |
| | for (std::list<const CVertex*>::iterator It = might_be_an_arc.begin(); |
| | It != might_be_an_arc.end(); |
| | It++) { |
| | const CVertex* vt = *It; |
| |
|
| | if (!c.LineIsOn(current_vt->m_p, vt->m_p, accuracy)) { |
| | return false; |
| | } |
| | current_vt = vt; |
| | } |
| |
|
| | CArc arc; |
| | arc.m_c = c.m_c; |
| | arc.m_s = prev_vt.m_p; |
| | arc.m_e = might_be_an_arc.back()->m_p; |
| | arc.SetDirWithPoint(might_be_an_arc.front()->m_p); |
| | arc.m_user_data = might_be_an_arc.back()->m_user_data; |
| |
|
| | double angs = atan2(arc.m_s.y - arc.m_c.y, arc.m_s.x - arc.m_c.x); |
| | double ange = atan2(arc.m_e.y - arc.m_c.y, arc.m_e.x - arc.m_c.x); |
| | if (arc.m_dir) { |
| | |
| | if (ange < angs) { |
| | ange += 6.2831853071795864; |
| | } |
| | } |
| | else { |
| | |
| | if (angs < ange) { |
| | angs += 6.2831853071795864; |
| | } |
| | } |
| |
|
| | if (arc.IncludedAngle() >= 3.15) { |
| | return false; |
| | } |
| |
|
| | for (std::list<const CVertex*>::iterator It = might_be_an_arc.begin(); |
| | It != might_be_an_arc.end(); |
| | It++) { |
| | const CVertex* vt = *It; |
| | double angp = atan2(vt->m_p.y - arc.m_c.y, vt->m_p.x - arc.m_c.x); |
| | if (arc.m_dir) { |
| | |
| | if (angp < angs) { |
| | angp += 6.2831853071795864; |
| | } |
| | if (angp > ange) { |
| | return false; |
| | } |
| | } |
| | else { |
| | |
| | if (angp < ange) { |
| | angp += 6.2831853071795864; |
| | } |
| | if (angp > angs) { |
| | return false; |
| | } |
| | } |
| | } |
| |
|
| | arc_returned = arc; |
| | return true; |
| | } |
| |
|
| | void CCurve::AddArcOrLines( |
| | bool check_for_arc, |
| | std::list<CVertex>& new_vertices, |
| | std::list<const CVertex*>& might_be_an_arc, |
| | CArc& arc, |
| | bool& arc_found, |
| | bool& arc_added |
| | ) |
| | { |
| | if (check_for_arc && CheckForArc(new_vertices.back(), might_be_an_arc, arc)) { |
| | arc_found = true; |
| | } |
| | else { |
| | if (arc_found) { |
| | if (arc.AlmostALine()) { |
| | new_vertices.emplace_back(arc.m_e, arc.m_user_data); |
| | } |
| | else { |
| | new_vertices.emplace_back(arc.m_dir ? 1 : -1, arc.m_e, arc.m_c, arc.m_user_data); |
| | } |
| |
|
| | arc_added = true; |
| | arc_found = false; |
| | const CVertex* back_vt = might_be_an_arc.back(); |
| | might_be_an_arc.clear(); |
| | if (check_for_arc) { |
| | might_be_an_arc.push_back(back_vt); |
| | } |
| | } |
| | else { |
| | const CVertex* back_vt = might_be_an_arc.back(); |
| | if (check_for_arc) { |
| | might_be_an_arc.pop_back(); |
| | } |
| | for (std::list<const CVertex*>::iterator It = might_be_an_arc.begin(); |
| | It != might_be_an_arc.end(); |
| | It++) { |
| | const CVertex* v = *It; |
| | if (It != might_be_an_arc.begin() || (new_vertices.size() == 0) |
| | || (new_vertices.back().m_p != v->m_p)) { |
| | new_vertices.push_back(*v); |
| | } |
| | } |
| | might_be_an_arc.clear(); |
| | if (check_for_arc) { |
| | might_be_an_arc.push_back(back_vt); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | void CCurve::FitArcs(bool retry) |
| | { |
| | std::list<CVertex> new_vertices; |
| |
|
| | std::list<const CVertex*> might_be_an_arc; |
| | CArc arc; |
| | bool arc_found = false; |
| | bool arc_added = false; |
| | int i = 0; |
| | for (std::list<CVertex>::iterator It = m_vertices.begin(); It != m_vertices.end(); It++, i++) { |
| | CVertex& vt = *It; |
| | if (vt.m_type || i == 0) { |
| | if (i != 0) { |
| | AddArcOrLines(false, new_vertices, might_be_an_arc, arc, arc_found, arc_added); |
| | } |
| | new_vertices.push_back(vt); |
| | } |
| | else { |
| | might_be_an_arc.push_back(&vt); |
| |
|
| | if (might_be_an_arc.size() == 1) { |
| | } |
| | else { |
| | AddArcOrLines(true, new_vertices, might_be_an_arc, arc, arc_found, arc_added); |
| | } |
| | } |
| | } |
| |
|
| | if (might_be_an_arc.size() > 0) { |
| | |
| | if (!retry && m_vertices.size() > 2 && m_vertices.begin()->m_type == 0 && IsClosed()) { |
| | std::list<const CVertex*> tmp; |
| | auto it = m_vertices.begin(); |
| | tmp.push_back(&(*it++)); |
| |
|
| | |
| | |
| | if (!arc_found || it->m_type == 0) { |
| | tmp.push_back(&(*it)); |
| | CArc tmpArc; |
| | auto itEnd = m_vertices.end(); |
| | --itEnd; |
| | --itEnd; |
| | if (CheckForArc(*itEnd, tmp, tmpArc)) { |
| | if (arc_found) { |
| | |
| | |
| |
|
| | |
| | |
| | |
| | m_vertices.pop_front(); |
| | m_vertices.push_back(m_vertices.front()); |
| | } |
| | else { |
| | m_vertices.push_front(CVertex(new_vertices.back().m_p)); |
| | m_vertices.pop_back(); |
| | } |
| | FitArcs(true); |
| | return; |
| | } |
| | } |
| | } |
| | AddArcOrLines(false, new_vertices, might_be_an_arc, arc, arc_found, arc_added); |
| | } |
| |
|
| | if (arc_added) { |
| | for (auto* v : might_be_an_arc) { |
| | new_vertices.push_back(*v); |
| | } |
| | m_vertices.swap(new_vertices); |
| | } |
| | } |
| |
|
| | void CCurve::UnFitArcs() |
| | { |
| | std::list<Point> new_pts; |
| |
|
| | const CVertex* prev_vertex = NULL; |
| | for (std::list<CVertex>::const_iterator It2 = m_vertices.begin(); It2 != m_vertices.end(); It2++) { |
| | const CVertex& vertex = *It2; |
| | if (vertex.m_type == 0 || prev_vertex == NULL) { |
| | new_pts.push_back(vertex.m_p * CArea::m_units); |
| | } |
| | else { |
| | if (vertex.m_p != prev_vertex->m_p) { |
| | double phi, dphi, dx, dy; |
| | int Segments; |
| | int i; |
| | double ang1, ang2, phit; |
| |
|
| | dx = (prev_vertex->m_p.x - vertex.m_c.x) * CArea::m_units; |
| | dy = (prev_vertex->m_p.y - vertex.m_c.y) * CArea::m_units; |
| |
|
| | ang1 = atan2(dy, dx); |
| | if (ang1 < 0) { |
| | ang1 += 2.0 * PI; |
| | } |
| | dx = (vertex.m_p.x - vertex.m_c.x) * CArea::m_units; |
| | dy = (vertex.m_p.y - vertex.m_c.y) * CArea::m_units; |
| | ang2 = atan2(dy, dx); |
| | if (ang2 < 0) { |
| | ang2 += 2.0 * PI; |
| | } |
| |
|
| | if (vertex.m_type == -1) { |
| | if (ang2 > ang1) { |
| | phit = 2.0 * PI - ang2 + ang1; |
| | } |
| | else { |
| | phit = ang1 - ang2; |
| | } |
| | } |
| | else { |
| | if (ang1 > ang2) { |
| | phit = -(2.0 * PI - ang1 + ang2); |
| | } |
| | else { |
| | phit = -(ang2 - ang1); |
| | } |
| | } |
| |
|
| | |
| | double radius = sqrt(dx * dx + dy * dy); |
| | dphi = 2 * acos((radius - CArea::m_accuracy) / radius); |
| |
|
| | |
| | if (phit > 0) { |
| | Segments = (int)ceil(phit / dphi); |
| | } |
| | else { |
| | Segments = (int)ceil(-phit / dphi); |
| | } |
| |
|
| | if (Segments < 1) { |
| | Segments = 1; |
| | } |
| | if (Segments > 100) { |
| | Segments = 100; |
| | } |
| |
|
| | dphi = phit / (Segments); |
| |
|
| | double px = prev_vertex->m_p.x * CArea::m_units; |
| | double py = prev_vertex->m_p.y * CArea::m_units; |
| |
|
| | for (i = 1; i <= Segments; i++) { |
| | dx = px - vertex.m_c.x * CArea::m_units; |
| | dy = py - vertex.m_c.y * CArea::m_units; |
| | phi = atan2(dy, dx); |
| |
|
| | double nx = vertex.m_c.x * CArea::m_units + radius * cos(phi - dphi); |
| | double ny = vertex.m_c.y * CArea::m_units + radius * sin(phi - dphi); |
| |
|
| | new_pts.emplace_back(nx, ny); |
| |
|
| | px = nx; |
| | py = ny; |
| | } |
| | } |
| | } |
| | prev_vertex = &vertex; |
| | } |
| |
|
| | m_vertices.clear(); |
| |
|
| | for (std::list<Point>::iterator It = new_pts.begin(); It != new_pts.end(); It++) { |
| | Point& pt = *It; |
| | CVertex vertex(0, pt / CArea::m_units, Point(0.0, 0.0)); |
| | m_vertices.push_back(vertex); |
| | } |
| | } |
| |
|
| | Point CCurve::NearestPoint(const Point& p) const |
| | { |
| | double best_dist = 0.0; |
| | Point best_point = Point(0, 0); |
| | bool best_point_valid = false; |
| | Point prev_p = Point(0, 0); |
| | bool prev_p_valid = false; |
| | bool first_span = true; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p_valid) { |
| | Point near_point = Span(prev_p, vertex, first_span).NearestPoint(p); |
| | first_span = false; |
| | double dist = near_point.dist(p); |
| | if (!best_point_valid || dist < best_dist) { |
| | best_dist = dist; |
| | best_point = near_point; |
| | best_point_valid = true; |
| | } |
| | } |
| | prev_p = vertex.m_p; |
| | prev_p_valid = true; |
| | } |
| | return best_point; |
| | } |
| |
|
| | Point CCurve::NearestPoint(const CCurve& c, double* d) const |
| | { |
| | double best_dist = 0.0; |
| | Point best_point = Point(0, 0); |
| | bool best_point_valid = false; |
| | Point prev_p = Point(0, 0); |
| | bool prev_p_valid = false; |
| | bool first_span = true; |
| | for (std::list<CVertex>::const_iterator It = c.m_vertices.begin(); It != c.m_vertices.end(); |
| | It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p_valid) { |
| | double dist; |
| | Point near_point = NearestPoint(Span(prev_p, vertex, first_span), &dist); |
| | first_span = false; |
| | if (!best_point_valid || dist < best_dist) { |
| | best_dist = dist; |
| | best_point = near_point; |
| | best_point_valid = true; |
| | } |
| | } |
| | prev_p = vertex.m_p; |
| | prev_p_valid = true; |
| | } |
| | if (d) { |
| | *d = best_dist; |
| | } |
| | return best_point; |
| | } |
| |
|
| | void CCurve::GetBox(CBox2D& box) |
| | { |
| | Point prev_p = Point(0, 0); |
| | bool prev_p_valid = false; |
| | for (std::list<CVertex>::iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | CVertex& vertex = *It; |
| | if (prev_p_valid) { |
| | Span(prev_p, vertex).GetBox(box); |
| | } |
| | prev_p = vertex.m_p; |
| | prev_p_valid = true; |
| | } |
| | } |
| |
|
| | void CCurve::Reverse() |
| | { |
| | std::list<CVertex> new_vertices; |
| |
|
| | CVertex* prev_v = NULL; |
| |
|
| | for (std::list<CVertex>::reverse_iterator It = m_vertices.rbegin(); It != m_vertices.rend(); |
| | It++) { |
| | CVertex& v = *It; |
| | int type = 0; |
| | Point cp(0.0, 0.0); |
| | if (prev_v) { |
| | type = -prev_v->m_type; |
| | cp = prev_v->m_c; |
| | } |
| | CVertex new_v(type, v.m_p, cp); |
| | new_vertices.push_back(new_v); |
| | prev_v = &v; |
| | } |
| |
|
| | m_vertices.swap(new_vertices); |
| | } |
| |
|
| | double CCurve::GetArea() const |
| | { |
| | double area = 0.0; |
| | Point prev_p = Point(0, 0); |
| | bool prev_p_valid = false; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p_valid) { |
| | area += Span(prev_p, vertex).GetArea(); |
| | } |
| | prev_p = vertex.m_p; |
| | prev_p_valid = true; |
| | } |
| | return area; |
| | } |
| |
|
| | bool CCurve::IsClosed() const |
| | { |
| | if (m_vertices.size() == 0) { |
| | return false; |
| | } |
| | return m_vertices.front().m_p == m_vertices.back().m_p; |
| | } |
| |
|
| | void CCurve::ChangeStart(const Point& p) |
| | { |
| | CCurve new_curve; |
| |
|
| | bool started = false; |
| | bool finished = false; |
| | int start_span = 0; |
| | bool closed = IsClosed(); |
| |
|
| | for (int i = 0; i < (closed ? 2 : 1); i++) { |
| | const Point* prev_p = NULL; |
| |
|
| | int span_index = 0; |
| | for (std::list<CVertex>::const_iterator VIt = m_vertices.begin(); |
| | VIt != m_vertices.end() && !finished; |
| | VIt++) { |
| | const CVertex& vertex = *VIt; |
| |
|
| | if (prev_p) { |
| | Span span(*prev_p, vertex); |
| | if (span.On(p)) { |
| | if (started) { |
| | if (p == *prev_p || span_index != start_span) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | else { |
| | if (p == vertex.m_p) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | else { |
| | CVertex v(vertex); |
| | v.m_p = p; |
| | new_curve.m_vertices.push_back(v); |
| | } |
| | finished = true; |
| | } |
| | } |
| | else { |
| | new_curve.m_vertices.emplace_back(p); |
| | started = true; |
| | start_span = span_index; |
| | if (p != vertex.m_p) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | } |
| | } |
| | else { |
| | if (started) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | } |
| | span_index++; |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| | } |
| |
|
| | if (started) { |
| | m_vertices.swap(new_curve.m_vertices); |
| | } |
| | } |
| |
|
| | void CCurve::Break(const Point& p) |
| | { |
| | |
| | const Point* prev_p = NULL; |
| |
|
| | for (std::list<CVertex>::iterator VIt = m_vertices.begin(); VIt != m_vertices.end(); VIt++) { |
| | CVertex& vertex = *VIt; |
| |
|
| | if (p == vertex.m_p) { |
| | break; |
| | } |
| |
|
| | if (prev_p) { |
| | Span span(*prev_p, vertex); |
| | if (span.On(p)) { |
| | CVertex v(vertex); |
| | v.m_p = p; |
| | m_vertices.insert(VIt, v); |
| | break; |
| | } |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| | } |
| |
|
| | void CCurve::ExtractSeparateCurves( |
| | const std::list<Point>& ordered_points, |
| | std::list<CCurve>& separate_curves |
| | ) const |
| | { |
| | |
| | |
| | const Point* prev_p = NULL; |
| |
|
| | if (ordered_points.size() == 0) { |
| | separate_curves.push_back(*this); |
| | return; |
| | } |
| |
|
| | CCurve current_curve; |
| |
|
| | std::list<Point>::const_iterator PIt = ordered_points.begin(); |
| | Point point = *PIt; |
| |
|
| | for (std::list<CVertex>::const_iterator VIt = m_vertices.begin(); VIt != m_vertices.end(); VIt++) { |
| | const CVertex& vertex = *VIt; |
| | if (prev_p) |
| | { |
| | Span span(*prev_p, vertex); |
| | while ((PIt != ordered_points.end()) && span.On(point)) { |
| | CVertex v(vertex); |
| | v.m_p = point; |
| | current_curve.m_vertices.push_back(v); |
| | if (current_curve.m_vertices.size() > 1) { |
| | separate_curves.push_back(current_curve); |
| | } |
| | current_curve = CCurve(); |
| | current_curve.m_vertices.push_back(v); |
| | PIt++; |
| | if (PIt != ordered_points.end()) { |
| | point = *PIt; |
| | } |
| | } |
| |
|
| | |
| | if (current_curve.m_vertices.back().m_p != vertex.m_p) { |
| | current_curve.m_vertices.push_back(vertex); |
| | } |
| | } |
| | if ((current_curve.m_vertices.size() == 0) |
| | || (current_curve.m_vertices.back().m_p != vertex.m_p)) { |
| | |
| | current_curve.m_vertices.push_back(vertex); |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| |
|
| | |
| | if (current_curve.m_vertices.size() > 1) { |
| | separate_curves.push_back(current_curve); |
| | } |
| | } |
| |
|
| |
|
| | void CCurve::RemoveTinySpans() |
| | { |
| | CCurve new_curve; |
| |
|
| | std::list<CVertex>::const_iterator VIt = m_vertices.begin(); |
| | new_curve.m_vertices.push_back(*VIt); |
| | VIt++; |
| |
|
| | for (; VIt != m_vertices.end(); VIt++) { |
| | const CVertex& vertex = *VIt; |
| |
|
| | if (vertex.m_type != 0 |
| | || new_curve.m_vertices.back().m_p.dist(vertex.m_p) > Point::tolerance) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | } |
| | m_vertices.swap(new_curve.m_vertices); |
| | } |
| |
|
| | void CCurve::ChangeEnd(const Point& p) |
| | { |
| | |
| | CCurve new_curve; |
| |
|
| | const Point* prev_p = NULL; |
| |
|
| | for (std::list<CVertex>::const_iterator VIt = m_vertices.begin(); VIt != m_vertices.end(); VIt++) { |
| | const CVertex& vertex = *VIt; |
| |
|
| | if (prev_p) { |
| | Span span(*prev_p, vertex); |
| | if (span.On(p)) { |
| | CVertex v(vertex); |
| | v.m_p = p; |
| | new_curve.m_vertices.push_back(v); |
| | break; |
| | } |
| | else { |
| | if (p != vertex.m_p) { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | } |
| | } |
| | else { |
| | new_curve.m_vertices.push_back(vertex); |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| |
|
| | m_vertices.swap(new_curve.m_vertices); |
| | } |
| |
|
| | Point CCurve::NearestPoint(const Span& p, double* d) const |
| | { |
| | double best_dist = 0.0; |
| | Point best_point = Point(0, 0); |
| | bool best_point_valid = false; |
| | Point prev_p = Point(0, 0); |
| | bool prev_p_valid = false; |
| | bool first_span = true; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p_valid) { |
| | double dist; |
| | Point near_point = Span(prev_p, vertex, first_span).NearestPoint(p, &dist); |
| | first_span = false; |
| | if (!best_point_valid || dist < best_dist) { |
| | best_dist = dist; |
| | best_point = near_point; |
| | best_point_valid = true; |
| | } |
| | } |
| | prev_p = vertex.m_p; |
| | prev_p_valid = true; |
| | } |
| | if (d) { |
| | *d = best_dist; |
| | } |
| | return best_point; |
| | } |
| |
|
| | static geoff_geometry::Kurve MakeKurve(const CCurve& curve) |
| | { |
| | geoff_geometry::Kurve k; |
| | for (std::list<CVertex>::const_iterator It = curve.m_vertices.begin(); |
| | It != curve.m_vertices.end(); |
| | It++) { |
| | const CVertex& v = *It; |
| | k.Add( |
| | geoff_geometry::spVertex( |
| | v.m_type, |
| | geoff_geometry::Point(v.m_p.x, v.m_p.y), |
| | geoff_geometry::Point(v.m_c.x, v.m_c.y) |
| | ) |
| | ); |
| | } |
| | return k; |
| | } |
| |
|
| | static CCurve MakeCCurve(const geoff_geometry::Kurve& k) |
| | { |
| | CCurve c; |
| | int n = k.nSpans(); |
| | for (int i = 0; i <= n; i++) { |
| | geoff_geometry::spVertex spv; |
| | k.Get(i, spv); |
| | c.append(CVertex(spv.type, Point(spv.p.x, spv.p.y), Point(spv.pc.x, spv.pc.y))); |
| | } |
| | return c; |
| | } |
| |
|
| | static geoff_geometry::Span MakeSpan(const Span& span) |
| | { |
| | return geoff_geometry::Span( |
| | span.m_v.m_type, |
| | geoff_geometry::Point(span.m_p.x, span.m_p.y), |
| | geoff_geometry::Point(span.m_v.m_p.x, span.m_v.m_p.y), |
| | geoff_geometry::Point(span.m_v.m_c.x, span.m_v.m_c.y) |
| | ); |
| | } |
| |
|
| | bool CCurve::Offset(double leftwards_value) |
| | { |
| | |
| | |
| | bool success = true; |
| |
|
| | CCurve save_curve = *this; |
| |
|
| | try { |
| | geoff_geometry::Kurve k = MakeKurve(*this); |
| | geoff_geometry::Kurve kOffset; |
| | int ret = 0; |
| | k.OffsetMethod1(kOffset, fabs(leftwards_value), (leftwards_value > 0) ? 1 : -1, 1, ret); |
| | success = (ret == 0); |
| | if (success) { |
| | *this = MakeCCurve(kOffset); |
| | } |
| | } |
| | catch (...) { |
| | success = false; |
| | } |
| |
|
| | if (!success) { |
| | if (this->IsClosed()) { |
| | double inwards_offset = leftwards_value; |
| | bool cw = false; |
| | if (this->IsClockwise()) { |
| | inwards_offset = -inwards_offset; |
| | cw = true; |
| | } |
| | CArea a; |
| | a.append(*this); |
| | a.Offset(inwards_offset); |
| | if (a.m_curves.size() == 1) { |
| | Span* start_span = NULL; |
| | if (this->m_vertices.size() > 1) { |
| | std::list<CVertex>::iterator It = m_vertices.begin(); |
| | CVertex& v0 = *It; |
| | It++; |
| | CVertex& v1 = *It; |
| | start_span = new Span(v0.m_p, v1, true); |
| | } |
| | *this = a.m_curves.front(); |
| | if (this->IsClockwise() != cw) { |
| | this->Reverse(); |
| | } |
| | if (start_span) { |
| | Point forward = start_span->GetVector(0.0); |
| | Point left(-forward.y, forward.x); |
| | Point offset_start = start_span->m_p + left * leftwards_value; |
| | this->ChangeStart(this->NearestPoint(offset_start)); |
| | delete start_span; |
| | } |
| | success = true; |
| | } |
| | } |
| | } |
| |
|
| | return success; |
| | } |
| |
|
| | void CCurve::GetSpans(std::list<Span>& spans) const |
| | { |
| | const Point* prev_p = NULL; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p) { |
| | spans.emplace_back(*prev_p, vertex); |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| | } |
| |
|
| | void CCurve::OffsetForward(double forwards_value, bool refit_arcs) |
| | { |
| | |
| |
|
| | |
| | UnFitArcs(); |
| |
|
| | std::list<Span> spans; |
| | GetSpans(spans); |
| |
|
| | m_vertices.clear(); |
| |
|
| | |
| | for (std::list<Span>::iterator It = spans.begin(); It != spans.end(); It++) { |
| | Span& span = *It; |
| | Point v = span.GetVector(0.0); |
| | v.normalize(); |
| | Point shift = v * forwards_value; |
| | span.m_p = span.m_p + shift; |
| | span.m_v.m_p = span.m_v.m_p + shift; |
| | } |
| |
|
| | |
| | for (std::list<Span>::iterator It = spans.begin(); It != spans.end();) { |
| | Span& span = *It; |
| | Point v = span.GetVector(0.0); |
| | v.normalize(); |
| |
|
| | |
| | if (It == spans.begin()) { |
| | m_vertices.push_back(span.m_p); |
| | } |
| | m_vertices.push_back(span.m_v.m_p); |
| |
|
| | It++; |
| | if (It != spans.end()) { |
| | Span& next_span = *It; |
| | Point nv = next_span.GetVector(0.0); |
| | nv.normalize(); |
| | double sin_angle = v ^ nv; |
| | bool sharp_corner = (fabs(sin_angle) > 0.5); |
| |
|
| | if (sharp_corner) { |
| | |
| | int arc_type = ((sin_angle > 0) ? 1 : (-1)); |
| | Point centre = span.m_v.m_p - v * forwards_value; |
| | m_vertices.emplace_back(arc_type, next_span.m_p, centre); |
| | } |
| | } |
| | } |
| |
|
| | if (refit_arcs) { |
| | FitArcs(); |
| | } |
| | else { |
| | UnFitArcs(); |
| | } |
| | } |
| |
|
| | double CCurve::Perim() const |
| | { |
| | const Point* prev_p = NULL; |
| | double perim = 0.0; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p) { |
| | Span span(*prev_p, vertex); |
| | perim += span.Length(); |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| |
|
| | return perim; |
| | } |
| |
|
| | Point CCurve::PerimToPoint(double perim) const |
| | { |
| | if (m_vertices.size() == 0) { |
| | return Point(0, 0); |
| | } |
| |
|
| | const Point* prev_p = NULL; |
| | double kperim = 0.0; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p) { |
| | Span span(*prev_p, vertex); |
| | double length = span.Length(); |
| | if (perim < kperim + length) { |
| | Point p = span.MidPerim(perim - kperim); |
| | return p; |
| | } |
| | kperim += length; |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| |
|
| | return m_vertices.back().m_p; |
| | } |
| |
|
| | double CCurve::PointToPerim(const Point& p) const |
| | { |
| | double best_dist = 0.0; |
| | double perim_at_best_dist = 0.0; |
| | |
| | bool best_dist_found = false; |
| |
|
| | double perim = 0.0; |
| |
|
| | const Point* prev_p = NULL; |
| | bool first_span = true; |
| | for (std::list<CVertex>::const_iterator It = m_vertices.begin(); It != m_vertices.end(); It++) { |
| | const CVertex& vertex = *It; |
| | if (prev_p) { |
| | Span span(*prev_p, vertex, first_span); |
| | Point near_point = span.NearestPoint(p); |
| | first_span = false; |
| | double dist = near_point.dist(p); |
| | if (!best_dist_found || dist < best_dist) { |
| | best_dist = dist; |
| | Span span_to_point(*prev_p, CVertex(span.m_v.m_type, near_point, span.m_v.m_c)); |
| | perim_at_best_dist = perim + span_to_point.Length(); |
| | best_dist_found = true; |
| | } |
| | perim += span.Length(); |
| | } |
| | prev_p = &(vertex.m_p); |
| | } |
| | return perim_at_best_dist; |
| | } |
| |
|
| | void CCurve::operator+=(const CCurve& curve) |
| | { |
| | for (std::list<CVertex>::const_iterator It = curve.m_vertices.begin(); |
| | It != curve.m_vertices.end(); |
| | It++) { |
| | const CVertex& vt = *It; |
| | if (It == curve.m_vertices.begin()) { |
| | if ((m_vertices.size() == 0) || (It->m_p != m_vertices.back().m_p)) { |
| | m_vertices.emplace_back(It->m_p); |
| | } |
| | } |
| | else { |
| | m_vertices.push_back(vt); |
| | } |
| | } |
| | } |
| |
|
| | void CCurve::CurveIntersections(const CCurve& c, std::list<Point>& pts) const |
| | { |
| | CArea a; |
| | a.append(*this); |
| | a.CurveIntersections(c, pts); |
| | } |
| |
|
| | void CCurve::SpanIntersections(const Span& s, std::list<Point>& pts) const |
| | { |
| | std::list<Span> spans; |
| | GetSpans(spans); |
| | for (std::list<Span>::iterator It = spans.begin(); It != spans.end(); It++) { |
| | Span& span = *It; |
| | std::list<Point> pts2; |
| | span.Intersect(s, pts2); |
| | for (std::list<Point>::iterator It = pts2.begin(); It != pts2.end(); It++) { |
| | Point& pt = *It; |
| | if (pts.size() == 0) { |
| | pts.push_back(pt); |
| | } |
| | else { |
| | if (pt != pts.back()) { |
| | pts.push_back(pt); |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|
| | const Point Span::null_point = Point(0, 0); |
| | const CVertex Span::null_vertex = CVertex(Point(0, 0)); |
| |
|
| | Span::Span() |
| | : m_start_span(false) |
| | , m_p(null_point) |
| | , m_v(null_vertex) |
| | {} |
| |
|
| | Point Span::NearestPointNotOnSpan(const Point& p) const |
| | { |
| | if (m_v.m_type == 0) { |
| | Point Vs = (m_v.m_p - m_p); |
| | Vs.normalize(); |
| | double dp = (p - m_p) * Vs; |
| | return (Vs * dp) + m_p; |
| | } |
| | else { |
| | double radius = m_p.dist(m_v.m_c); |
| | double r = p.dist(m_v.m_c); |
| | if (r < Point::tolerance) { |
| | return m_p; |
| | } |
| | Point vc = (m_v.m_c - p); |
| | return p + vc * ((r - radius) / r); |
| | } |
| | } |
| |
|
| | Point Span::NearestPoint(const Point& p) const |
| | { |
| | Point np = NearestPointNotOnSpan(p); |
| | double t = Parameter(np); |
| | if (t >= 0.0 && t <= 1.0) { |
| | return np; |
| | } |
| |
|
| | double d1 = p.dist(this->m_p); |
| | double d2 = p.dist(this->m_v.m_p); |
| |
|
| | if (d1 < d2) { |
| | return this->m_p; |
| | } |
| | else { |
| | return m_v.m_p; |
| | } |
| | } |
| |
|
| | Point Span::MidPerim(double d) const |
| | { |
| | |
| | Point p; |
| | if (m_v.m_type == 0) { |
| | Point vs = m_v.m_p - m_p; |
| | vs.normalize(); |
| | p = vs * d + m_p; |
| | } |
| | else { |
| | Point v = m_p - m_v.m_c; |
| | double radius = v.length(); |
| | v.Rotate(d * m_v.m_type / radius); |
| | p = v + m_v.m_c; |
| | } |
| | return p; |
| | } |
| |
|
| | Point Span::MidParam(double param) const |
| | { |
| | |
| | if (fabs(param) < 0.00000000000001) { |
| | return m_p; |
| | } |
| | if (fabs(param - 1.0) < 0.00000000000001) { |
| | return m_v.m_p; |
| | } |
| |
|
| | Point p; |
| | if (m_v.m_type == 0) { |
| | Point vs = m_v.m_p - m_p; |
| | p = vs * param + m_p; |
| | } |
| | else { |
| | Point v = m_p - m_v.m_c; |
| | v.Rotate(param * IncludedAngle()); |
| | p = v + m_v.m_c; |
| | } |
| | return p; |
| | } |
| |
|
| | Point Span::NearestPointToSpan(const Span& p, double& d) const |
| | { |
| | Point midpoint = MidParam(0.5); |
| | Point np = p.NearestPoint(m_p); |
| | Point best_point = m_p; |
| | double dist = np.dist(m_p); |
| | if (p.m_start_span) { |
| | dist -= (CArea::m_accuracy * 2); |
| | } |
| | Point npm = p.NearestPoint(midpoint); |
| | double dm = npm.dist(midpoint) |
| | - CArea::m_accuracy; |
| | if (dm < dist) { |
| | dist = dm; |
| | best_point = midpoint; |
| | } |
| | Point np2 = p.NearestPoint(m_v.m_p); |
| | double dp2 = np2.dist(m_v.m_p); |
| | if (dp2 < dist) { |
| | dist = dp2; |
| | best_point = m_v.m_p; |
| | } |
| | d = dist; |
| | return best_point; |
| | } |
| |
|
| | Point Span::NearestPoint(const Span& p, double* d) const |
| | { |
| | double best_dist; |
| | Point best_point = this->NearestPointToSpan(p, best_dist); |
| |
|
| | |
| | double best_dist2; |
| | Point best_point2 = p.NearestPointToSpan(*this, best_dist2); |
| | if (best_dist2 < best_dist) { |
| | best_point = NearestPoint(best_point2); |
| | best_dist = best_dist2; |
| | } |
| |
|
| | if (d) { |
| | *d = best_dist; |
| | } |
| | return best_point; |
| | } |
| |
|
| | static int GetQuadrant(const Point& v) |
| | { |
| | |
| | if (v.x > 0) { |
| | if (v.y > 0) { |
| | return 0; |
| | } |
| | return 3; |
| | } |
| | if (v.y > 0) { |
| | return 1; |
| | } |
| | return 2; |
| | } |
| |
|
| | static Point QuadrantEndPoint(int i) |
| | { |
| | if (i > 3) { |
| | i -= 4; |
| | } |
| | switch (i) { |
| | case 0: |
| | return Point(0.0, 1.0); |
| | case 1: |
| | return Point(-1.0, 0.0); |
| | case 2: |
| | return Point(0.0, -1.0); |
| | default: |
| | return Point(1.0, 0.0); |
| | } |
| | } |
| |
|
| | void Span::GetBox(CBox2D& box) |
| | { |
| | box.Insert(m_p); |
| | box.Insert(m_v.m_p); |
| |
|
| | if (this->m_v.m_type) { |
| | |
| | Point vs = m_p - m_v.m_c; |
| | Point ve = m_v.m_p - m_v.m_c; |
| | int qs = GetQuadrant(vs); |
| | int qe = GetQuadrant(ve); |
| | if (m_v.m_type == -1) { |
| | |
| | int t = qs; |
| | qs = qe; |
| | qe = t; |
| | } |
| |
|
| | if (qe < qs) { |
| | qe = qe + 4; |
| | } |
| |
|
| | double rad = m_v.m_p.dist(m_v.m_c); |
| |
|
| | for (int i = qs; i < qe; i++) { |
| | box.Insert(m_v.m_c + QuadrantEndPoint(i) * rad); |
| | } |
| | } |
| | } |
| |
|
| | double IncludedAngle(const Point& v0, const Point& v1, int dir) |
| | { |
| | |
| | double inc_ang = v0 * v1; |
| | if (inc_ang > 1. - 1.0e-10) { |
| | return 0; |
| | } |
| | if (inc_ang < -1. + 1.0e-10) { |
| | inc_ang = PI; |
| | } |
| | else { |
| | if (inc_ang > 1.0) { |
| | inc_ang = 1.0; |
| | } |
| | inc_ang = acos(inc_ang); |
| |
|
| | if (dir * (v0 ^ v1) < 0) { |
| | inc_ang = 2 * PI - inc_ang; |
| | } |
| | } |
| | return dir * inc_ang; |
| | } |
| |
|
| | double Span::IncludedAngle() const |
| | { |
| | if (m_v.m_type) { |
| | Point vs = ~(m_p - m_v.m_c); |
| | Point ve = ~(m_v.m_p - m_v.m_c); |
| | if (m_v.m_type == -1) { |
| | vs = -vs; |
| | ve = -ve; |
| | } |
| | vs.normalize(); |
| | ve.normalize(); |
| |
|
| | return ::IncludedAngle(vs, ve, m_v.m_type); |
| | } |
| |
|
| | return 0.0; |
| | } |
| |
|
| | double Span::GetArea() const |
| | { |
| | if (m_v.m_type) { |
| | double angle = IncludedAngle(); |
| | double radius = m_p.dist(m_v.m_c); |
| | return ( |
| | 0.5 |
| | * ((m_v.m_c.x - m_p.x) * (m_v.m_c.y + m_p.y) |
| | - (m_v.m_c.x - m_v.m_p.x) * (m_v.m_c.y + m_v.m_p.y) - angle * radius * radius) |
| | ); |
| | } |
| |
|
| | return 0.5 * (m_v.m_p.x - m_p.x) * (m_p.y + m_v.m_p.y); |
| | } |
| |
|
| | double Span::Parameter(const Point& p) const |
| | { |
| | double t; |
| | if (m_v.m_type == 0) { |
| | Point v0 = p - m_p; |
| | Point vs = m_v.m_p - m_p; |
| | double length = vs.length(); |
| | vs.normalize(); |
| | t = vs * v0; |
| | t = t / length; |
| | } |
| | else { |
| | |
| | Point vs = ~(m_p - m_v.m_c); |
| | Point v = ~(p - m_v.m_c); |
| | vs.normalize(); |
| | v.normalize(); |
| | if (m_v.m_type == -1) { |
| | vs = -vs; |
| | v = -v; |
| | } |
| | double ang = ::IncludedAngle(vs, v, m_v.m_type); |
| | double angle = IncludedAngle(); |
| | t = ang / angle; |
| | } |
| | return t; |
| | } |
| |
|
| | bool Span::On(const Point& p, double* t) const |
| | { |
| | if (p != NearestPoint(p)) { |
| | return false; |
| | } |
| | if (t) { |
| | *t = Parameter(p); |
| | } |
| | return true; |
| | } |
| |
|
| | double Span::Length() const |
| | { |
| | if (m_v.m_type) { |
| | double radius = m_p.dist(m_v.m_c); |
| | return fabs(IncludedAngle()) * radius; |
| | } |
| |
|
| | return m_p.dist(m_v.m_p); |
| | } |
| |
|
| | Point Span::GetVector(double fraction) const |
| | { |
| | |
| | if (m_v.m_type == 0) { |
| | Point v(m_p, m_v.m_p); |
| | v.normalize(); |
| | return v; |
| | } |
| |
|
| | Point p = MidParam(fraction); |
| | Point v(m_v.m_c, p); |
| | v.normalize(); |
| | if (m_v.m_type == 1) { |
| | return Point(-v.y, v.x); |
| | } |
| | else { |
| | return Point(v.y, -v.x); |
| | } |
| | } |
| |
|
| | void Span::Intersect(const Span& s, std::list<Point>& pts) const |
| | { |
| | |
| | geoff_geometry::Point pInt1, pInt2; |
| | double t[4]; |
| | int num_int = MakeSpan(*this).Intof(MakeSpan(s), pInt1, pInt2, t); |
| | if (num_int > 0) { |
| | pts.emplace_back(pInt1.x, pInt1.y); |
| | } |
| | if (num_int > 1) { |
| | pts.emplace_back(pInt2.x, pInt2.y); |
| | } |
| | } |
| |
|
| | void tangential_arc(const Point& p0, const Point& p1, const Point& v0, Point& c, int& dir) |
| | { |
| | geoff_geometry::Point gp0(p0.x, p0.y); |
| | geoff_geometry::Point gp1(p1.x, p1.y); |
| | geoff_geometry::Vector2d gv0(v0.x, v0.y); |
| | geoff_geometry::Point gc; |
| | geoff_geometry::tangential_arc(gp0, gp1, gv0, gc, dir); |
| | c = Point(gc.x, gc.y); |
| | } |
| |
|