965 lines
29 KiB
C++
965 lines
29 KiB
C++
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Area.cpp
|
|
|
|
// Copyright 2011, Dan Heeks
|
|
// This program is released under the BSD license. See the file COPYING for details.
|
|
|
|
#include "Area.h"
|
|
#include "AreaOrderer.h"
|
|
|
|
#include <limits>
|
|
#include <map>
|
|
|
|
double CArea::m_accuracy = 0.01;
|
|
double CArea::m_units = 1.0;
|
|
bool CArea::m_clipper_simple = false;
|
|
double CArea::m_clipper_clean_distance = 0.0;
|
|
bool CArea::m_fit_arcs = true;
|
|
int CArea::m_min_arc_points = 4;
|
|
int CArea::m_max_arc_points = 100;
|
|
double CArea::m_single_area_processing_length = 0.0;
|
|
double CArea::m_processing_done = 0.0;
|
|
bool CArea::m_please_abort = false;
|
|
double CArea::m_MakeOffsets_increment = 0.0;
|
|
double CArea::m_split_processing_length = 0.0;
|
|
bool CArea::m_set_processing_length_in_split = false;
|
|
double CArea::m_after_MakeOffsets_length = 0.0;
|
|
// static const double PI = 3.1415926535897932;
|
|
|
|
#define _CAREA_PARAM_DEFINE(_class, _type, _name) \
|
|
_type CArea::get_##_name() \
|
|
{ \
|
|
return _class::_name; \
|
|
} \
|
|
void CArea::set_##_name(_type _name) \
|
|
{ \
|
|
_class::_name = _name; \
|
|
}
|
|
|
|
#define CAREA_PARAM_DEFINE(_type, _name) \
|
|
_type CArea::get_##_name() \
|
|
{ \
|
|
return m_##_name; \
|
|
} \
|
|
void CArea::set_##_name(_type _name) \
|
|
{ \
|
|
m_##_name = _name; \
|
|
}
|
|
|
|
_CAREA_PARAM_DEFINE(Point, double, tolerance)
|
|
CAREA_PARAM_DEFINE(bool, fit_arcs)
|
|
CAREA_PARAM_DEFINE(bool, clipper_simple)
|
|
CAREA_PARAM_DEFINE(double, clipper_clean_distance)
|
|
CAREA_PARAM_DEFINE(double, accuracy)
|
|
CAREA_PARAM_DEFINE(double, units)
|
|
CAREA_PARAM_DEFINE(short, min_arc_points)
|
|
CAREA_PARAM_DEFINE(short, max_arc_points)
|
|
CAREA_PARAM_DEFINE(double, clipper_scale)
|
|
|
|
void CArea::append(const CCurve& curve)
|
|
{
|
|
m_curves.push_back(curve);
|
|
}
|
|
|
|
void CArea::move(CCurve&& curve)
|
|
{
|
|
m_curves.push_back(std::move(curve));
|
|
}
|
|
|
|
void CArea::FitArcs()
|
|
{
|
|
for (std::list<CCurve>::iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
CCurve& curve = *It;
|
|
curve.FitArcs();
|
|
}
|
|
}
|
|
|
|
Point CArea::NearestPoint(const Point& p) const
|
|
{
|
|
double best_dist = 0.0;
|
|
Point best_point = Point(0, 0);
|
|
for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
const CCurve& curve = *It;
|
|
Point near_point = curve.NearestPoint(p);
|
|
double dist = near_point.dist(p);
|
|
if (It == m_curves.begin() || dist < best_dist) {
|
|
best_dist = dist;
|
|
best_point = near_point;
|
|
}
|
|
}
|
|
return best_point;
|
|
}
|
|
|
|
void CArea::ChangeStartToNearest(const Point* point, double min_dist)
|
|
{
|
|
for (std::list<CCurve>::iterator It = m_curves.begin(), ItNext = It; It != m_curves.end();
|
|
It = ItNext) {
|
|
++ItNext;
|
|
if (It->m_vertices.size() <= 1) {
|
|
m_curves.erase(It);
|
|
}
|
|
}
|
|
|
|
if (m_curves.empty()) {
|
|
return;
|
|
}
|
|
|
|
std::list<CCurve> curves;
|
|
Point p;
|
|
if (point) {
|
|
p = *point;
|
|
}
|
|
if (min_dist < Point::tolerance) {
|
|
min_dist = Point::tolerance;
|
|
}
|
|
|
|
while (m_curves.size()) {
|
|
std::list<CCurve>::iterator It = m_curves.begin();
|
|
std::list<CCurve>::iterator ItBest = It++;
|
|
Point best_point = ItBest->NearestPoint(p);
|
|
double best_dist = p.dist(best_point);
|
|
for (; It != m_curves.end(); ++It) {
|
|
const CCurve& curve = *It;
|
|
Point near_point;
|
|
double dist;
|
|
if (min_dist > Point::tolerance && !curve.IsClosed()) {
|
|
double d1 = curve.m_vertices.front().m_p.dist(p);
|
|
double d2 = curve.m_vertices.back().m_p.dist(p);
|
|
if (d1 < d2) {
|
|
dist = d1;
|
|
near_point = curve.m_vertices.front().m_p;
|
|
}
|
|
else {
|
|
dist = d2;
|
|
near_point = curve.m_vertices.back().m_p;
|
|
}
|
|
}
|
|
else {
|
|
near_point = curve.NearestPoint(p);
|
|
dist = near_point.dist(p);
|
|
}
|
|
if (dist < best_dist) {
|
|
best_dist = dist;
|
|
best_point = near_point;
|
|
ItBest = It;
|
|
}
|
|
}
|
|
if (ItBest->IsClosed()) {
|
|
ItBest->ChangeStart(best_point);
|
|
}
|
|
else {
|
|
double dfront = ItBest->m_vertices.front().m_p.dist(best_point);
|
|
double dback = ItBest->m_vertices.back().m_p.dist(best_point);
|
|
if (min_dist > Point::tolerance && dfront > min_dist && dback > min_dist) {
|
|
ItBest->Break(best_point);
|
|
m_curves.push_back(*ItBest);
|
|
m_curves.back().ChangeEnd(best_point);
|
|
ItBest->ChangeStart(best_point);
|
|
}
|
|
else if (dfront > dback) {
|
|
ItBest->Reverse();
|
|
}
|
|
}
|
|
curves.splice(curves.end(), m_curves, ItBest);
|
|
p = curves.back().m_vertices.back().m_p;
|
|
}
|
|
m_curves.splice(m_curves.end(), curves);
|
|
}
|
|
|
|
|
|
void CArea::GetBox(CBox2D& box)
|
|
{
|
|
for (std::list<CCurve>::iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
CCurve& curve = *It;
|
|
curve.GetBox(box);
|
|
}
|
|
}
|
|
|
|
void CArea::Reorder()
|
|
{
|
|
// curves may have been added with wrong directions
|
|
// test all kurves to see which one are outsides and which are insides and
|
|
// make sure outsides are anti-clockwise and insides are clockwise
|
|
|
|
// returns 0, if the curves are OK
|
|
// returns 1, if the curves are overlapping
|
|
|
|
CAreaOrderer ao;
|
|
for (std::list<CCurve>::iterator It = m_curves.begin(), ItNext = It; It != m_curves.end();
|
|
It = ItNext) {
|
|
++ItNext;
|
|
CCurve& curve = *It;
|
|
if (!It->IsClosed()) {
|
|
continue;
|
|
}
|
|
ao.Insert(make_shared<CCurve>(curve));
|
|
if (m_set_processing_length_in_split) {
|
|
CArea::m_processing_done += (m_split_processing_length / m_curves.size());
|
|
}
|
|
m_curves.erase(It);
|
|
}
|
|
|
|
if (ao.m_top_level) {
|
|
ao.m_top_level->GetArea(*this);
|
|
}
|
|
}
|
|
|
|
class ZigZag
|
|
{
|
|
public:
|
|
CCurve zig;
|
|
CCurve zag;
|
|
ZigZag(const CCurve& Zig, const CCurve& Zag)
|
|
: zig(Zig)
|
|
, zag(Zag)
|
|
{}
|
|
};
|
|
|
|
static double stepover_for_pocket = 0.0;
|
|
static std::list<ZigZag> zigzag_list_for_zigs;
|
|
static std::list<CCurve>* curve_list_for_zigs = NULL;
|
|
static bool rightward_for_zigs = true;
|
|
static double sin_angle_for_zigs = 0.0;
|
|
static double cos_angle_for_zigs = 0.0;
|
|
static double sin_minus_angle_for_zigs = 0.0;
|
|
static double cos_minus_angle_for_zigs = 0.0;
|
|
static double one_over_units = 0.0;
|
|
|
|
static Point rotated_point(const Point& p)
|
|
{
|
|
return Point(
|
|
p.x * cos_angle_for_zigs - p.y * sin_angle_for_zigs,
|
|
p.x * sin_angle_for_zigs + p.y * cos_angle_for_zigs
|
|
);
|
|
}
|
|
|
|
static Point unrotated_point(const Point& p)
|
|
{
|
|
return Point(
|
|
p.x * cos_minus_angle_for_zigs - p.y * sin_minus_angle_for_zigs,
|
|
p.x * sin_minus_angle_for_zigs + p.y * cos_minus_angle_for_zigs
|
|
);
|
|
}
|
|
|
|
static CVertex rotated_vertex(const CVertex& v)
|
|
{
|
|
if (v.m_type) {
|
|
return CVertex(v.m_type, rotated_point(v.m_p), rotated_point(v.m_c));
|
|
}
|
|
return CVertex(v.m_type, rotated_point(v.m_p), Point(0, 0));
|
|
}
|
|
|
|
static CVertex unrotated_vertex(const CVertex& v)
|
|
{
|
|
if (v.m_type) {
|
|
return CVertex(v.m_type, unrotated_point(v.m_p), unrotated_point(v.m_c));
|
|
}
|
|
return CVertex(v.m_type, unrotated_point(v.m_p), Point(0, 0));
|
|
}
|
|
|
|
static void rotate_area(CArea& a)
|
|
{
|
|
for (std::list<CCurve>::iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) {
|
|
CCurve& curve = *It;
|
|
for (std::list<CVertex>::iterator CIt = curve.m_vertices.begin();
|
|
CIt != curve.m_vertices.end();
|
|
CIt++) {
|
|
CVertex& vt = *CIt;
|
|
vt = rotated_vertex(vt);
|
|
}
|
|
}
|
|
}
|
|
|
|
void test_y_point(int i, const Point& p, Point& best_p, bool& found, int& best_index, double y, bool left_not_right)
|
|
{
|
|
// only consider points at y
|
|
if (fabs(p.y - y) < 0.002 * one_over_units) {
|
|
if (found) {
|
|
// equal high point
|
|
if (left_not_right) {
|
|
// use the furthest left point
|
|
if (p.x < best_p.x) {
|
|
best_p = p;
|
|
best_index = i;
|
|
}
|
|
}
|
|
else {
|
|
// use the furthest right point
|
|
if (p.x > best_p.x) {
|
|
best_p = p;
|
|
best_index = i;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
best_p = p;
|
|
best_index = i;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void make_zig_curve(const CCurve& input_curve, double y0, double y)
|
|
{
|
|
CCurve curve(input_curve);
|
|
|
|
if (rightward_for_zigs) {
|
|
if (curve.IsClockwise()) {
|
|
curve.Reverse();
|
|
}
|
|
}
|
|
else {
|
|
if (!curve.IsClockwise()) {
|
|
curve.Reverse();
|
|
}
|
|
}
|
|
|
|
// find a high point to start looking from
|
|
Point top_left;
|
|
int top_left_index = 0;
|
|
bool top_left_found = false;
|
|
Point top_right;
|
|
int top_right_index = 0;
|
|
bool top_right_found = false;
|
|
Point bottom_left;
|
|
int bottom_left_index = 0;
|
|
bool bottom_left_found = false;
|
|
|
|
int i = 0;
|
|
for (std::list<CVertex>::const_iterator VIt = curve.m_vertices.begin();
|
|
VIt != curve.m_vertices.end();
|
|
VIt++, i++) {
|
|
const CVertex& vertex = *VIt;
|
|
|
|
test_y_point(i, vertex.m_p, top_right, top_right_found, top_right_index, y, !rightward_for_zigs);
|
|
test_y_point(i, vertex.m_p, top_left, top_left_found, top_left_index, y, rightward_for_zigs);
|
|
test_y_point(i, vertex.m_p, bottom_left, bottom_left_found, bottom_left_index, y0, rightward_for_zigs);
|
|
}
|
|
|
|
int start_index = 0;
|
|
int end_index = 0;
|
|
int zag_end_index = 0;
|
|
|
|
if (bottom_left_found) {
|
|
start_index = bottom_left_index;
|
|
}
|
|
else if (top_left_found) {
|
|
start_index = top_left_index;
|
|
}
|
|
|
|
if (top_right_found) {
|
|
end_index = top_right_index;
|
|
zag_end_index = top_left_index;
|
|
}
|
|
else {
|
|
end_index = bottom_left_index;
|
|
zag_end_index = bottom_left_index;
|
|
}
|
|
if (end_index <= start_index) {
|
|
end_index += (i - 1);
|
|
}
|
|
if (zag_end_index <= start_index) {
|
|
zag_end_index += (i - 1);
|
|
}
|
|
|
|
CCurve zig, zag;
|
|
|
|
bool zig_started = false;
|
|
bool zig_finished = false;
|
|
bool zag_finished = false;
|
|
|
|
int v_index = 0;
|
|
for (int i = 0; i < 2; i++) {
|
|
// process the curve twice because we don't know where it will start
|
|
if (zag_finished) {
|
|
break;
|
|
}
|
|
for (std::list<CVertex>::const_iterator VIt = curve.m_vertices.begin();
|
|
VIt != curve.m_vertices.end();
|
|
VIt++) {
|
|
if (i == 1 && VIt == curve.m_vertices.begin()) {
|
|
continue;
|
|
}
|
|
|
|
const CVertex& vertex = *VIt;
|
|
|
|
if (zig_finished) {
|
|
zag.m_vertices.push_back(unrotated_vertex(vertex));
|
|
if (v_index == zag_end_index) {
|
|
zag_finished = true;
|
|
break;
|
|
}
|
|
}
|
|
else if (zig_started) {
|
|
zig.m_vertices.push_back(unrotated_vertex(vertex));
|
|
if (v_index == end_index) {
|
|
zig_finished = true;
|
|
if (v_index == zag_end_index) {
|
|
zag_finished = true;
|
|
break;
|
|
}
|
|
zag.m_vertices.push_back(unrotated_vertex(vertex));
|
|
}
|
|
}
|
|
else {
|
|
if (v_index == start_index) {
|
|
zig.m_vertices.push_back(unrotated_vertex(vertex));
|
|
zig_started = true;
|
|
}
|
|
}
|
|
v_index++;
|
|
}
|
|
}
|
|
|
|
if (zig_finished) {
|
|
zigzag_list_for_zigs.emplace_back(zig, zag);
|
|
}
|
|
}
|
|
|
|
void make_zig(const CArea& a, double y0, double y)
|
|
{
|
|
for (std::list<CCurve>::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) {
|
|
const CCurve& curve = *It;
|
|
make_zig_curve(curve, y0, y);
|
|
}
|
|
}
|
|
|
|
std::list<std::list<ZigZag>> reorder_zig_list_list;
|
|
|
|
void add_reorder_zig(ZigZag& zigzag)
|
|
{
|
|
// look in existing lists
|
|
|
|
// see if the zag is part of an existing zig
|
|
if (zigzag.zag.m_vertices.size() > 1) {
|
|
const Point& zag_e = zigzag.zag.m_vertices.front().m_p;
|
|
bool zag_removed = false;
|
|
for (std::list<std::list<ZigZag>>::iterator It = reorder_zig_list_list.begin();
|
|
It != reorder_zig_list_list.end() && !zag_removed;
|
|
It++) {
|
|
std::list<ZigZag>& zigzag_list = *It;
|
|
for (std::list<ZigZag>::iterator It2 = zigzag_list.begin();
|
|
It2 != zigzag_list.end() && !zag_removed;
|
|
It2++) {
|
|
const ZigZag& z = *It2;
|
|
for (std::list<CVertex>::const_iterator It3 = z.zig.m_vertices.begin();
|
|
It3 != z.zig.m_vertices.end() && !zag_removed;
|
|
It3++) {
|
|
const CVertex& v = *It3;
|
|
if ((fabs(zag_e.x - v.m_p.x) < (0.002 * one_over_units))
|
|
&& (fabs(zag_e.y - v.m_p.y) < (0.002 * one_over_units))) {
|
|
// remove zag from zigzag
|
|
zigzag.zag.m_vertices.clear();
|
|
zag_removed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// see if the zigzag can join the end of an existing list
|
|
const Point& zig_s = zigzag.zig.m_vertices.front().m_p;
|
|
for (std::list<std::list<ZigZag>>::iterator It = reorder_zig_list_list.begin();
|
|
It != reorder_zig_list_list.end();
|
|
It++) {
|
|
std::list<ZigZag>& zigzag_list = *It;
|
|
const ZigZag& last_zigzag = zigzag_list.back();
|
|
const Point& e = last_zigzag.zig.m_vertices.back().m_p;
|
|
if ((fabs(zig_s.x - e.x) < (0.002 * one_over_units))
|
|
&& (fabs(zig_s.y - e.y) < (0.002 * one_over_units))) {
|
|
zigzag_list.push_back(zigzag);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// else add a new list
|
|
std::list<ZigZag> zigzag_list;
|
|
zigzag_list.push_back(zigzag);
|
|
reorder_zig_list_list.push_back(zigzag_list);
|
|
}
|
|
|
|
void reorder_zigs()
|
|
{
|
|
for (std::list<ZigZag>::iterator It = zigzag_list_for_zigs.begin();
|
|
It != zigzag_list_for_zigs.end();
|
|
It++) {
|
|
ZigZag& zigzag = *It;
|
|
add_reorder_zig(zigzag);
|
|
}
|
|
|
|
zigzag_list_for_zigs.clear();
|
|
|
|
for (std::list<std::list<ZigZag>>::iterator It = reorder_zig_list_list.begin();
|
|
It != reorder_zig_list_list.end();
|
|
It++) {
|
|
std::list<ZigZag>& zigzag_list = *It;
|
|
if (zigzag_list.size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
curve_list_for_zigs->push_back(CCurve());
|
|
for (std::list<ZigZag>::const_iterator It = zigzag_list.begin(); It != zigzag_list.end();) {
|
|
const ZigZag& zigzag = *It;
|
|
for (std::list<CVertex>::const_iterator It2 = zigzag.zig.m_vertices.begin();
|
|
It2 != zigzag.zig.m_vertices.end();
|
|
It2++) {
|
|
if (It2 == zigzag.zig.m_vertices.begin() && It != zigzag_list.begin()) {
|
|
continue; // only add the first vertex if doing the first zig
|
|
}
|
|
const CVertex& v = *It2;
|
|
curve_list_for_zigs->back().m_vertices.push_back(v);
|
|
}
|
|
|
|
It++;
|
|
if (It == zigzag_list.end()) {
|
|
for (std::list<CVertex>::const_iterator It2 = zigzag.zag.m_vertices.begin();
|
|
It2 != zigzag.zag.m_vertices.end();
|
|
It2++) {
|
|
if (It2 == zigzag.zag.m_vertices.begin()) {
|
|
continue; // don't add the first vertex of the zag
|
|
}
|
|
const CVertex& v = *It2;
|
|
curve_list_for_zigs->back().m_vertices.push_back(v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
reorder_zig_list_list.clear();
|
|
}
|
|
|
|
static void zigzag(const CArea& input_a)
|
|
{
|
|
if (input_a.m_curves.size() == 0) {
|
|
CArea::m_processing_done += CArea::m_single_area_processing_length;
|
|
return;
|
|
}
|
|
|
|
one_over_units = 1 / CArea::m_units;
|
|
|
|
CArea a(input_a);
|
|
rotate_area(a);
|
|
|
|
CBox2D b;
|
|
a.GetBox(b);
|
|
|
|
double x0 = b.MinX() - 1.0;
|
|
double x1 = b.MaxX() + 1.0;
|
|
|
|
double height = b.MaxY() - b.MinY();
|
|
int num_steps = int(height / stepover_for_pocket + 1);
|
|
double y = b.MinY(); // + 0.1 * one_over_units;
|
|
Point null_point(0, 0);
|
|
rightward_for_zigs = true;
|
|
|
|
if (CArea::m_please_abort) {
|
|
return;
|
|
}
|
|
|
|
double step_percent_increment = 0.8 * CArea::m_single_area_processing_length / num_steps;
|
|
|
|
for (int i = 0; i < num_steps; i++) {
|
|
double y0 = y;
|
|
y = y + stepover_for_pocket;
|
|
Point p0(x0, y0);
|
|
Point p1(x0, y);
|
|
Point p2(x1, y);
|
|
Point p3(x1, y0);
|
|
CCurve c;
|
|
c.m_vertices.emplace_back(0, p0, null_point, 0);
|
|
c.m_vertices.emplace_back(0, p1, null_point, 0);
|
|
c.m_vertices.emplace_back(0, p2, null_point, 1);
|
|
c.m_vertices.emplace_back(0, p3, null_point, 0);
|
|
c.m_vertices.emplace_back(0, p0, null_point, 1);
|
|
CArea a2;
|
|
a2.m_curves.push_back(c);
|
|
a2.Intersect(a);
|
|
make_zig(a2, y0, y);
|
|
rightward_for_zigs = !rightward_for_zigs;
|
|
if (CArea::m_please_abort) {
|
|
return;
|
|
}
|
|
CArea::m_processing_done += step_percent_increment;
|
|
}
|
|
|
|
reorder_zigs();
|
|
CArea::m_processing_done += 0.2 * CArea::m_single_area_processing_length;
|
|
}
|
|
|
|
void CArea::SplitAndMakePocketToolpath(std::list<CCurve>& curve_list, const CAreaPocketParams& params) const
|
|
{
|
|
CArea::m_processing_done = 0.0;
|
|
|
|
double save_units = CArea::m_units;
|
|
CArea::m_units = 1.0;
|
|
std::list<CArea> areas;
|
|
m_split_processing_length = 50.0; // jump to 50 percent after split
|
|
m_set_processing_length_in_split = true;
|
|
Split(areas);
|
|
m_set_processing_length_in_split = false;
|
|
CArea::m_processing_done = m_split_processing_length;
|
|
CArea::m_units = save_units;
|
|
|
|
if (areas.size() == 0) {
|
|
return;
|
|
}
|
|
|
|
double single_area_length = 50.0 / areas.size();
|
|
|
|
for (std::list<CArea>::iterator It = areas.begin(); It != areas.end(); It++) {
|
|
CArea::m_single_area_processing_length = single_area_length;
|
|
CArea& ar = *It;
|
|
ar.MakePocketToolpath(curve_list, params);
|
|
}
|
|
}
|
|
|
|
void CArea::MakePocketToolpath(std::list<CCurve>& curve_list, const CAreaPocketParams& params) const
|
|
{
|
|
double radians_angle = params.zig_angle * PI / 180;
|
|
sin_angle_for_zigs = sin(-radians_angle);
|
|
cos_angle_for_zigs = cos(-radians_angle);
|
|
sin_minus_angle_for_zigs = sin(radians_angle);
|
|
cos_minus_angle_for_zigs = cos(radians_angle);
|
|
stepover_for_pocket = params.stepover;
|
|
|
|
CArea a_offset = *this;
|
|
double current_offset = params.tool_radius + params.extra_offset;
|
|
|
|
a_offset.Offset(current_offset);
|
|
|
|
if (params.mode == ZigZagPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) {
|
|
curve_list_for_zigs = &curve_list;
|
|
zigzag(a_offset);
|
|
}
|
|
else if (params.mode == SpiralPocketMode) {
|
|
std::list<CArea> m_areas;
|
|
a_offset.Split(m_areas);
|
|
if (CArea::m_please_abort) {
|
|
return;
|
|
}
|
|
if (m_areas.size() == 0) {
|
|
CArea::m_processing_done += CArea::m_single_area_processing_length;
|
|
return;
|
|
}
|
|
|
|
CArea::m_single_area_processing_length /= m_areas.size();
|
|
|
|
for (std::list<CArea>::iterator It = m_areas.begin(); It != m_areas.end(); It++) {
|
|
CArea& a2 = *It;
|
|
a2.MakeOnePocketCurve(curve_list, params);
|
|
}
|
|
}
|
|
|
|
if (params.mode == SingleOffsetPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) {
|
|
// if there are already curves, attempt to start the offset from the current tool position
|
|
bool done = false;
|
|
if (!curve_list.empty() && !curve_list.back().m_vertices.empty()) {
|
|
// find the closest curve to the start point
|
|
const Point start = curve_list.back().m_vertices.back().m_p;
|
|
auto curve_itmin = a_offset.m_curves.begin();
|
|
double dmin = Point::tolerance;
|
|
for (auto it = a_offset.m_curves.begin(); it != a_offset.m_curves.end(); it++) {
|
|
const double dist = it->NearestPoint(start).dist(start);
|
|
if (dist < dmin) {
|
|
dmin = dist;
|
|
curve_itmin = it;
|
|
}
|
|
}
|
|
|
|
// if the start point is on that curve (within Point::tolerance), do the profile
|
|
// starting on that curve
|
|
if (dmin < Point::tolerance) {
|
|
// split the curve into two parts -- starting with this point, and ending with this
|
|
// point
|
|
CCurve startCurve;
|
|
CCurve endCurve;
|
|
|
|
std::list<Span> spans;
|
|
curve_itmin->GetSpans(spans);
|
|
int imin = -1;
|
|
double dmin = std::numeric_limits<double>::max();
|
|
Point nmin;
|
|
Span smin;
|
|
{
|
|
int i = 0;
|
|
for (auto it = spans.begin(); it != spans.end(); i++, it++) {
|
|
const Point nearest = it->NearestPoint(start);
|
|
const double dist = nearest.dist(start);
|
|
if (dist < dmin) {
|
|
dmin = dist;
|
|
imin = i;
|
|
nmin = nearest;
|
|
smin = *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
startCurve.append(CVertex(nmin));
|
|
endCurve.append(curve_itmin->m_vertices.front());
|
|
{
|
|
int i = 0;
|
|
for (auto it = spans.begin(); it != spans.end(); i++, it++) {
|
|
if (i < imin) {
|
|
endCurve.append(it->m_v);
|
|
}
|
|
else if (i > imin) {
|
|
startCurve.append(it->m_v);
|
|
}
|
|
else {
|
|
if (nmin != endCurve.m_vertices.back().m_p) {
|
|
endCurve.append(
|
|
CVertex(smin.m_v.m_type, nmin, smin.m_v.m_c, smin.m_v.m_user_data)
|
|
);
|
|
}
|
|
if (nmin != it->m_v.m_p) {
|
|
startCurve.append(
|
|
CVertex(smin.m_v.m_type, it->m_v.m_p, smin.m_v.m_c, smin.m_v.m_user_data)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// append curves to the curve list: start curve, other curves wrapping around, end
|
|
// curve
|
|
const auto appendCurve = [&curve_list](const CCurve& curve) {
|
|
if (curve_list.size() > 0
|
|
&& curve_list.back().m_vertices.back().m_p == curve.m_vertices.front().m_p) {
|
|
auto it = curve.m_vertices.begin();
|
|
for (it++; it != curve.m_vertices.end(); it++) {
|
|
curve_list.back().append(*it);
|
|
}
|
|
}
|
|
else {
|
|
curve_list.push_back(curve);
|
|
}
|
|
};
|
|
|
|
if (startCurve.m_vertices.size() > 1) {
|
|
appendCurve(startCurve);
|
|
}
|
|
{
|
|
auto it = curve_itmin;
|
|
for (it++; it != a_offset.m_curves.end(); it++) {
|
|
appendCurve(*it);
|
|
}
|
|
}
|
|
for (auto it = a_offset.m_curves.begin(); it != curve_itmin; it++) {
|
|
appendCurve(*it);
|
|
}
|
|
if (endCurve.m_vertices.size() > 1) {
|
|
appendCurve(endCurve);
|
|
}
|
|
|
|
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
// add the single offset too
|
|
if (!done) {
|
|
for (std::list<CCurve>::iterator It = a_offset.m_curves.begin();
|
|
It != a_offset.m_curves.end();
|
|
It++) {
|
|
CCurve& curve = *It;
|
|
curve_list.push_back(curve);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CArea::Split(std::list<CArea>& m_areas) const
|
|
{
|
|
if (HolesLinked()) {
|
|
for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
const CCurve& curve = *It;
|
|
m_areas.emplace_back();
|
|
m_areas.back().m_curves.push_back(curve);
|
|
}
|
|
}
|
|
else {
|
|
CArea a = *this;
|
|
a.Reorder();
|
|
|
|
if (CArea::m_please_abort) {
|
|
return;
|
|
}
|
|
|
|
for (std::list<CCurve>::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) {
|
|
const CCurve& curve = *It;
|
|
if (curve.IsClockwise()) {
|
|
if (m_areas.size() > 0) {
|
|
m_areas.back().m_curves.push_back(curve);
|
|
}
|
|
}
|
|
else {
|
|
m_areas.emplace_back();
|
|
m_areas.back().m_curves.push_back(curve);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
double CArea::GetArea(bool always_add) const
|
|
{
|
|
// returns the area of the area
|
|
double area = 0.0;
|
|
for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
const CCurve& curve = *It;
|
|
double a = curve.GetArea();
|
|
if (always_add) {
|
|
area += fabs(a);
|
|
}
|
|
else {
|
|
area += a;
|
|
}
|
|
}
|
|
return area;
|
|
}
|
|
|
|
eOverlapType GetOverlapType(const CCurve& c1, const CCurve& c2)
|
|
{
|
|
CArea a1;
|
|
a1.m_curves.push_back(c1);
|
|
CArea a2;
|
|
a2.m_curves.push_back(c2);
|
|
|
|
return GetOverlapType(a1, a2);
|
|
}
|
|
|
|
eOverlapType GetOverlapType(const CArea& a1, const CArea& a2)
|
|
{
|
|
CArea A1(a1);
|
|
|
|
A1.Subtract(a2);
|
|
if (A1.m_curves.size() == 0) {
|
|
return eInside;
|
|
}
|
|
|
|
CArea A2(a2);
|
|
A2.Subtract(a1);
|
|
if (A2.m_curves.size() == 0) {
|
|
return eOutside;
|
|
}
|
|
|
|
A1 = a1;
|
|
A1.Intersect(a2);
|
|
if (A1.m_curves.size() == 0) {
|
|
return eSiblings;
|
|
}
|
|
|
|
return eCrossing;
|
|
}
|
|
|
|
bool IsInside(const Point& p, const CCurve& c)
|
|
{
|
|
CArea a;
|
|
a.m_curves.push_back(c);
|
|
return IsInside(p, a);
|
|
}
|
|
|
|
bool IsInside(const Point& p, const CArea& a)
|
|
{
|
|
CArea a2;
|
|
CCurve c;
|
|
c.m_vertices.emplace_back(Point(p.x - 0.01, p.y - 0.01));
|
|
c.m_vertices.emplace_back(Point(p.x + 0.01, p.y - 0.01));
|
|
c.m_vertices.emplace_back(Point(p.x + 0.01, p.y + 0.01));
|
|
c.m_vertices.emplace_back(Point(p.x - 0.01, p.y + 0.01));
|
|
c.m_vertices.emplace_back(Point(p.x - 0.01, p.y - 0.01));
|
|
a2.m_curves.push_back(c);
|
|
a2.Intersect(a);
|
|
if (fabs(a2.GetArea()) < 0.0004) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CArea::SpanIntersections(const Span& span, std::list<Point>& pts) const
|
|
{
|
|
// this returns all the intersections of this area with the given span, ordered along the span
|
|
|
|
// get all points where this area's curves intersect the span
|
|
std::list<Point> pts2;
|
|
for (std::list<CCurve>::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) {
|
|
const CCurve& c = *It;
|
|
c.SpanIntersections(span, pts2);
|
|
}
|
|
|
|
// order them along the span
|
|
std::multimap<double, Point> ordered_points;
|
|
for (std::list<Point>::iterator It = pts2.begin(); It != pts2.end(); It++) {
|
|
Point& p = *It;
|
|
double t;
|
|
if (span.On(p, &t)) {
|
|
ordered_points.insert(std::make_pair(t, p));
|
|
}
|
|
}
|
|
|
|
// add them to the given list of points
|
|
for (std::multimap<double, Point>::iterator It = ordered_points.begin();
|
|
It != ordered_points.end();
|
|
It++) {
|
|
Point p = It->second;
|
|
pts.push_back(p);
|
|
}
|
|
}
|
|
|
|
void CArea::CurveIntersections(const CCurve& curve, std::list<Point>& pts) const
|
|
{
|
|
// this returns all the intersections of this area with the given curve, ordered along the curve
|
|
std::list<Span> spans;
|
|
curve.GetSpans(spans);
|
|
for (std::list<Span>::iterator It = spans.begin(); It != spans.end(); It++) {
|
|
Span& span = *It;
|
|
std::list<Point> pts2;
|
|
SpanIntersections(span, 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ThickLine
|
|
{
|
|
public:
|
|
CArea m_area;
|
|
CCurve m_curve;
|
|
|
|
ThickLine(const CCurve& curve)
|
|
{
|
|
m_curve = curve;
|
|
m_area.append(curve);
|
|
m_area.Thicken(0.001);
|
|
}
|
|
};
|
|
|
|
void CArea::InsideCurves(const CCurve& curve, std::list<CCurve>& curves_inside) const
|
|
{
|
|
// 1. find the intersectionpoints between these two curves.
|
|
std::list<Point> pts;
|
|
CurveIntersections(curve, pts);
|
|
|
|
// 2.separate curve2 in multiple curves between these intersections.
|
|
std::list<CCurve> separate_curves;
|
|
curve.ExtractSeparateCurves(pts, separate_curves);
|
|
|
|
// 3. if the midpoint of a separate curve lies in a1, then we return it.
|
|
for (std::list<CCurve>::iterator It = separate_curves.begin(); It != separate_curves.end(); It++) {
|
|
CCurve& curve = *It;
|
|
double length = curve.Perim();
|
|
Point mid_point = curve.PerimToPoint(length * 0.5);
|
|
if (IsInside(mid_point, *this)) {
|
|
curves_inside.push_back(curve);
|
|
}
|
|
}
|
|
}
|