├── Arc.cpp ├── Arc.h ├── Area.cpp ├── Area.h ├── AreaClipper.cpp ├── AreaDxf.cpp ├── AreaDxf.h ├── AreaOrderer.cpp ├── AreaOrderer.h ├── AreaPocket.cpp ├── Box2D.h ├── CMakeLists.txt ├── Circle.cpp ├── Circle.h ├── Curve.cpp ├── Curve.h ├── License.txt ├── Point.h ├── PythonStuff.cpp ├── PythonStuff.h ├── README ├── area clipper.sln ├── area clipper.vcproj ├── area.sln ├── area.vcproj ├── clipper.cpp ├── clipper.hpp ├── debian ├── changelog ├── compat ├── control ├── copyright ├── libarea-dev.install ├── libarea0.install ├── python-area.install ├── rules └── source │ └── format ├── dxf.cpp ├── dxf.h ├── kurve ├── Construction.cpp ├── Finite.cpp ├── Matrix.cpp ├── geometry.h ├── kurve.cpp └── offset.cpp ├── test.bat └── test.py /Arc.cpp: -------------------------------------------------------------------------------- 1 | // Arc.cpp 2 | 3 | // Copyright 2011, Dan Heeks 4 | // This program is released under the BSD license. See the file COPYING for details. 5 | 6 | #include "Arc.h" 7 | #include "Curve.h" 8 | 9 | void CArc::SetDirWithPoint(const Point& p) 10 | { 11 | double angs = atan2(m_s.y - m_c.y, m_s.x - m_c.x); 12 | double ange = atan2(m_e.y - m_c.y, m_e.x - m_c.x); 13 | double angp = atan2(p.y - m_c.y, p.x - m_c.x); 14 | if(ange < angs)ange += 6.2831853071795864; 15 | if(angp < angs - 0.0000000000001)angp += 6.2831853071795864; 16 | if(angp > ange + 0.0000000000001)m_dir = false; 17 | else m_dir = true; 18 | } 19 | 20 | double CArc::IncludedAngle()const 21 | { 22 | double angs = atan2(m_s.y - m_c.y, m_s.x - m_c.x); 23 | double ange = atan2(m_e.y - m_c.y, m_e.x - m_c.x); 24 | if(m_dir) 25 | { 26 | // make sure ange > angs 27 | if(ange < angs)ange += 6.2831853071795864; 28 | } 29 | else 30 | { 31 | // make sure angs > ange 32 | if(angs < ange)angs += 6.2831853071795864; 33 | } 34 | 35 | return fabs(ange - angs); 36 | } 37 | 38 | bool CArc::AlmostALine()const 39 | { 40 | Point mid_point = MidParam(0.5); 41 | if(Line(m_s, m_e - m_s).Dist(mid_point) <= Point::tolerance) 42 | return true; 43 | 44 | const double max_arc_radius = 1.0 / Point::tolerance; 45 | double radius = m_c.dist(m_s); 46 | if (radius > max_arc_radius) 47 | { 48 | return true; // We don't want to produce an arc whose radius is too large. 49 | } 50 | 51 | return false; 52 | } 53 | 54 | Point CArc::MidParam(double param)const { 55 | /// returns a point which is 0-1 along arc 56 | if(fabs(param) < 0.00000000000001)return m_s; 57 | if(fabs(param - 1.0) < 0.00000000000001)return m_e; 58 | 59 | Point p; 60 | Point v = m_s - m_c; 61 | v.Rotate(param * IncludedAngle()); 62 | p = v + m_c; 63 | 64 | return p; 65 | } 66 | 67 | //segments - number of segments per full revolution! 68 | //d_angle - determines the direction and the ammount of the arc to draw 69 | void CArc::GetSegments(void(*callbackfunc)(const double *p), double pixels_per_mm, bool want_start_point)const 70 | { 71 | if(m_s == m_e) 72 | return; 73 | 74 | Point Va = m_s - m_c; 75 | Point Vb = m_e - m_c; 76 | 77 | double start_angle = atan2(Va.y, Va.x); 78 | double end_angle = atan2(Vb.y, Vb.x); 79 | 80 | if(m_dir) 81 | { 82 | if(start_angle > end_angle)end_angle += 6.28318530717958; 83 | } 84 | else 85 | { 86 | if(start_angle < end_angle)end_angle -= 6.28318530717958; 87 | } 88 | 89 | double radius = m_c.dist(m_s); 90 | double d_angle = end_angle - start_angle; 91 | int segments = (int)(fabs(pixels_per_mm * radius * d_angle / 6.28318530717958 + 1)); 92 | 93 | double theta = d_angle / (double)segments; 94 | while(theta>1.0){segments*=2;theta = d_angle / (double)segments;} 95 | double tangetial_factor = tan(theta); 96 | double radial_factor = 1 - cos(theta); 97 | 98 | double x = radius * cos(start_angle); 99 | double y = radius * sin(start_angle); 100 | 101 | double pp[3] = {0.0, 0.0, 0.0}; 102 | 103 | for(int i = 0; i < segments + 1; i++) 104 | { 105 | Point p = m_c + Point(x, y); 106 | pp[0] = p.x; 107 | pp[1] = p.y; 108 | (*callbackfunc)(pp); 109 | 110 | double tx = -y; 111 | double ty = x; 112 | 113 | x += tx * tangetial_factor; 114 | y += ty * tangetial_factor; 115 | 116 | double rx = - x; 117 | double ry = - y; 118 | 119 | x += rx * radial_factor; 120 | y += ry * radial_factor; 121 | } 122 | } -------------------------------------------------------------------------------- /Arc.h: -------------------------------------------------------------------------------- 1 | // Arc.h 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | 7 | #include "Point.h" 8 | 9 | class CArc{ 10 | public: 11 | Point m_s; 12 | Point m_e; 13 | Point m_c; 14 | bool m_dir; // true - anti-clockwise, false - clockwise 15 | int m_user_data; 16 | 17 | CArc():m_dir(true), m_user_data(0){} 18 | CArc(const Point& s, const Point& e, const Point& c, bool dir, int user_data):m_s(s), m_e(e), m_c(c), m_dir(dir), m_user_data(user_data){} 19 | 20 | void SetDirWithPoint(const Point& p); // set m_dir, such that this point lies between m_s and m_e 21 | double IncludedAngle()const; // always > 0 22 | bool AlmostALine()const; 23 | Point MidParam(double param)const; 24 | void GetSegments(void(*callbackfunc)(const double *p), double pixels_per_mm, bool want_start_point = true)const; 25 | }; -------------------------------------------------------------------------------- /Area.cpp: -------------------------------------------------------------------------------- 1 | // Area.cpp 2 | 3 | // Copyright 2011, Dan Heeks 4 | // This program is released under the BSD license. See the file COPYING for details. 5 | 6 | #include "Area.h" 7 | #include "AreaOrderer.h" 8 | 9 | #include 10 | 11 | double CArea::m_accuracy = 0.01; 12 | double CArea::m_units = 1.0; 13 | bool CArea::m_fit_arcs = true; 14 | double CArea::m_single_area_processing_length = 0.0; 15 | double CArea::m_processing_done = 0.0; 16 | bool CArea::m_please_abort = false; 17 | double CArea::m_MakeOffsets_increment = 0.0; 18 | double CArea::m_split_processing_length = 0.0; 19 | bool CArea::m_set_processing_length_in_split = false; 20 | double CArea::m_after_MakeOffsets_length = 0.0; 21 | //static const double PI = 3.1415926535897932; 22 | 23 | void CArea::append(const CCurve& curve) 24 | { 25 | m_curves.push_back(curve); 26 | } 27 | 28 | void CArea::FitArcs(){ 29 | for(std::list::iterator It = m_curves.begin(); It != m_curves.end(); It++) 30 | { 31 | CCurve& curve = *It; 32 | curve.FitArcs(); 33 | } 34 | } 35 | 36 | Point CArea::NearestPoint(const Point& p)const 37 | { 38 | double best_dist = 0.0; 39 | Point best_point = Point(0, 0); 40 | for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) 41 | { 42 | const CCurve& curve = *It; 43 | Point near_point = curve.NearestPoint(p); 44 | double dist = near_point.dist(p); 45 | if(It == m_curves.begin() || dist < best_dist) 46 | { 47 | best_dist = dist; 48 | best_point = near_point; 49 | } 50 | } 51 | return best_point; 52 | } 53 | 54 | void CArea::GetBox(CBox2D &box) 55 | { 56 | for(std::list::iterator It = m_curves.begin(); It != m_curves.end(); It++) 57 | { 58 | CCurve& curve = *It; 59 | curve.GetBox(box); 60 | } 61 | } 62 | 63 | void CArea::Reorder() 64 | { 65 | // curves may have been added with wrong directions 66 | // test all kurves to see which one are outsides and which are insides and 67 | // make sure outsides are anti-clockwise and insides are clockwise 68 | 69 | // returns 0, if the curves are OK 70 | // returns 1, if the curves are overlapping 71 | 72 | CAreaOrderer ao; 73 | for(std::list::iterator It = m_curves.begin(); It != m_curves.end(); It++) 74 | { 75 | CCurve& curve = *It; 76 | ao.Insert(&curve); 77 | if(m_set_processing_length_in_split) 78 | { 79 | CArea::m_processing_done += (m_split_processing_length / m_curves.size()); 80 | } 81 | } 82 | 83 | *this = ao.ResultArea(); 84 | } 85 | 86 | class ZigZag 87 | { 88 | public: 89 | CCurve zig; 90 | CCurve zag; 91 | ZigZag(const CCurve& Zig, const CCurve& Zag):zig(Zig), zag(Zag){} 92 | }; 93 | 94 | static double stepover_for_pocket = 0.0; 95 | static std::list zigzag_list_for_zigs; 96 | static std::list *curve_list_for_zigs = NULL; 97 | static bool rightward_for_zigs = true; 98 | static double sin_angle_for_zigs = 0.0; 99 | static double cos_angle_for_zigs = 0.0; 100 | static double sin_minus_angle_for_zigs = 0.0; 101 | static double cos_minus_angle_for_zigs = 0.0; 102 | static double one_over_units = 0.0; 103 | 104 | static Point rotated_point(const Point &p) 105 | { 106 | 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); 107 | } 108 | 109 | static Point unrotated_point(const Point &p) 110 | { 111 | 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); 112 | } 113 | 114 | static CVertex rotated_vertex(const CVertex &v) 115 | { 116 | if(v.m_type) 117 | { 118 | return CVertex(v.m_type, rotated_point(v.m_p), rotated_point(v.m_c)); 119 | } 120 | return CVertex(v.m_type, rotated_point(v.m_p), Point(0, 0)); 121 | } 122 | 123 | static CVertex unrotated_vertex(const CVertex &v) 124 | { 125 | if(v.m_type) 126 | { 127 | return CVertex(v.m_type, unrotated_point(v.m_p), unrotated_point(v.m_c)); 128 | } 129 | return CVertex(v.m_type, unrotated_point(v.m_p), Point(0, 0)); 130 | } 131 | 132 | static void rotate_area(CArea &a) 133 | { 134 | for(std::list::iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) 135 | { 136 | CCurve& curve = *It; 137 | for(std::list::iterator CIt = curve.m_vertices.begin(); CIt != curve.m_vertices.end(); CIt++) 138 | { 139 | CVertex& vt = *CIt; 140 | vt = rotated_vertex(vt); 141 | } 142 | } 143 | } 144 | 145 | void test_y_point(int i, const Point& p, Point& best_p, bool &found, int &best_index, double y, bool left_not_right) 146 | { 147 | // only consider points at y 148 | if(fabs(p.y - y) < 0.002 * one_over_units) 149 | { 150 | if(found) 151 | { 152 | // equal high point 153 | if(left_not_right) 154 | { 155 | // use the furthest left point 156 | if(p.x < best_p.x) 157 | { 158 | best_p = p; 159 | best_index = i; 160 | } 161 | } 162 | else 163 | { 164 | // use the furthest right point 165 | if(p.x > best_p.x) 166 | { 167 | best_p = p; 168 | best_index = i; 169 | } 170 | } 171 | } 172 | else 173 | { 174 | best_p = p; 175 | best_index = i; 176 | found = true; 177 | } 178 | } 179 | } 180 | 181 | static void make_zig_curve(const CCurve& input_curve, double y0, double y) 182 | { 183 | CCurve curve(input_curve); 184 | 185 | if(rightward_for_zigs) 186 | { 187 | if(curve.IsClockwise()) 188 | curve.Reverse(); 189 | } 190 | else 191 | { 192 | if(!curve.IsClockwise()) 193 | curve.Reverse(); 194 | } 195 | 196 | // find a high point to start looking from 197 | Point top_left; 198 | int top_left_index; 199 | bool top_left_found = false; 200 | Point top_right; 201 | int top_right_index; 202 | bool top_right_found = false; 203 | Point bottom_left; 204 | int bottom_left_index; 205 | bool bottom_left_found = false; 206 | 207 | int i =0; 208 | for(std::list::const_iterator VIt = curve.m_vertices.begin(); VIt != curve.m_vertices.end(); VIt++, i++) 209 | { 210 | const CVertex& vertex = *VIt; 211 | 212 | test_y_point(i, vertex.m_p, top_right, top_right_found, top_right_index, y, !rightward_for_zigs); 213 | test_y_point(i, vertex.m_p, top_left, top_left_found, top_left_index, y, rightward_for_zigs); 214 | test_y_point(i, vertex.m_p, bottom_left, bottom_left_found, bottom_left_index, y0, rightward_for_zigs); 215 | } 216 | 217 | int start_index = 0; 218 | int end_index = 0; 219 | int zag_end_index = 0; 220 | 221 | if(bottom_left_found)start_index = bottom_left_index; 222 | else if(top_left_found)start_index = top_left_index; 223 | 224 | if(top_right_found) 225 | { 226 | end_index = top_right_index; 227 | zag_end_index = top_left_index; 228 | } 229 | else 230 | { 231 | end_index = bottom_left_index; 232 | zag_end_index = bottom_left_index; 233 | } 234 | if(end_index <= start_index)end_index += (i-1); 235 | if(zag_end_index <= start_index)zag_end_index += (i-1); 236 | 237 | CCurve zig, zag; 238 | 239 | bool zig_started = false; 240 | bool zig_finished = false; 241 | bool zag_finished = false; 242 | 243 | int v_index = 0; 244 | for(int i = 0; i < 2; i++) 245 | { 246 | // process the curve twice because we don't know where it will start 247 | if(zag_finished) 248 | break; 249 | for(std::list::const_iterator VIt = curve.m_vertices.begin(); VIt != curve.m_vertices.end(); VIt++) 250 | { 251 | if(i == 1 && VIt == curve.m_vertices.begin()) 252 | { 253 | continue; 254 | } 255 | 256 | const CVertex& vertex = *VIt; 257 | 258 | if(zig_finished) 259 | { 260 | zag.m_vertices.push_back(unrotated_vertex(vertex)); 261 | if(v_index == zag_end_index) 262 | { 263 | zag_finished = true; 264 | break; 265 | } 266 | } 267 | else if(zig_started) 268 | { 269 | zig.m_vertices.push_back(unrotated_vertex(vertex)); 270 | if(v_index == end_index) 271 | { 272 | zig_finished = true; 273 | if(v_index == zag_end_index) 274 | { 275 | zag_finished = true; 276 | break; 277 | } 278 | zag.m_vertices.push_back(unrotated_vertex(vertex)); 279 | } 280 | } 281 | else 282 | { 283 | if(v_index == start_index) 284 | { 285 | zig.m_vertices.push_back(unrotated_vertex(vertex)); 286 | zig_started = true; 287 | } 288 | } 289 | v_index++; 290 | } 291 | } 292 | 293 | if(zig_finished) 294 | zigzag_list_for_zigs.push_back(ZigZag(zig, zag)); 295 | } 296 | 297 | void make_zig(const CArea &a, double y0, double y) 298 | { 299 | for(std::list::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) 300 | { 301 | const CCurve &curve = *It; 302 | make_zig_curve(curve, y0, y); 303 | } 304 | } 305 | 306 | std::list< std::list > reorder_zig_list_list; 307 | 308 | void add_reorder_zig(ZigZag &zigzag) 309 | { 310 | // look in existing lists 311 | 312 | // see if the zag is part of an existing zig 313 | if(zigzag.zag.m_vertices.size() > 1) 314 | { 315 | const Point& zag_e = zigzag.zag.m_vertices.front().m_p; 316 | bool zag_removed = false; 317 | for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end() && !zag_removed; It++) 318 | { 319 | std::list &zigzag_list = *It; 320 | for(std::list::iterator It2 = zigzag_list.begin(); It2 != zigzag_list.end() && !zag_removed; It2++) 321 | { 322 | const ZigZag& z = *It2; 323 | for(std::list::const_iterator It3 = z.zig.m_vertices.begin(); It3 != z.zig.m_vertices.end() && !zag_removed; It3++) 324 | { 325 | const CVertex &v = *It3; 326 | 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))) 327 | { 328 | // remove zag from zigzag 329 | zigzag.zag.m_vertices.clear(); 330 | zag_removed = true; 331 | } 332 | } 333 | } 334 | } 335 | } 336 | 337 | // see if the zigzag can join the end of an existing list 338 | const Point& zig_s = zigzag.zig.m_vertices.front().m_p; 339 | for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end(); It++) 340 | { 341 | std::list &zigzag_list = *It; 342 | const ZigZag& last_zigzag = zigzag_list.back(); 343 | const Point& e = last_zigzag.zig.m_vertices.back().m_p; 344 | if((fabs(zig_s.x - e.x) < (0.002 * one_over_units)) && (fabs(zig_s.y - e.y) < (0.002 * one_over_units))) 345 | { 346 | zigzag_list.push_back(zigzag); 347 | return; 348 | } 349 | } 350 | 351 | // else add a new list 352 | std::list zigzag_list; 353 | zigzag_list.push_back(zigzag); 354 | reorder_zig_list_list.push_back(zigzag_list); 355 | } 356 | 357 | void reorder_zigs() 358 | { 359 | for(std::list::iterator It = zigzag_list_for_zigs.begin(); It != zigzag_list_for_zigs.end(); It++) 360 | { 361 | ZigZag &zigzag = *It; 362 | add_reorder_zig(zigzag); 363 | } 364 | 365 | zigzag_list_for_zigs.clear(); 366 | 367 | for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end(); It++) 368 | { 369 | std::list &zigzag_list = *It; 370 | if(zigzag_list.size() == 0)continue; 371 | 372 | curve_list_for_zigs->push_back(CCurve()); 373 | for(std::list::const_iterator It = zigzag_list.begin(); It != zigzag_list.end();) 374 | { 375 | const ZigZag &zigzag = *It; 376 | for(std::list::const_iterator It2 = zigzag.zig.m_vertices.begin(); It2 != zigzag.zig.m_vertices.end(); It2++) 377 | { 378 | if(It2 == zigzag.zig.m_vertices.begin() && It != zigzag_list.begin())continue; // only add the first vertex if doing the first zig 379 | const CVertex &v = *It2; 380 | curve_list_for_zigs->back().m_vertices.push_back(v); 381 | } 382 | 383 | It++; 384 | if(It == zigzag_list.end()) 385 | { 386 | for(std::list::const_iterator It2 = zigzag.zag.m_vertices.begin(); It2 != zigzag.zag.m_vertices.end(); It2++) 387 | { 388 | if(It2 == zigzag.zag.m_vertices.begin())continue; // don't add the first vertex of the zag 389 | const CVertex &v = *It2; 390 | curve_list_for_zigs->back().m_vertices.push_back(v); 391 | } 392 | } 393 | } 394 | } 395 | reorder_zig_list_list.clear(); 396 | } 397 | 398 | static void zigzag(const CArea &input_a) 399 | { 400 | if(input_a.m_curves.size() == 0) 401 | { 402 | CArea::m_processing_done += CArea::m_single_area_processing_length; 403 | return; 404 | } 405 | 406 | one_over_units = 1 / CArea::m_units; 407 | 408 | CArea a(input_a); 409 | rotate_area(a); 410 | 411 | CBox2D b; 412 | a.GetBox(b); 413 | 414 | double x0 = b.MinX() - 1.0; 415 | double x1 = b.MaxX() + 1.0; 416 | 417 | double height = b.MaxY() - b.MinY(); 418 | int num_steps = int(height / stepover_for_pocket + 1); 419 | double y = b.MinY();// + 0.1 * one_over_units; 420 | Point null_point(0, 0); 421 | rightward_for_zigs = true; 422 | 423 | if(CArea::m_please_abort)return; 424 | 425 | double step_percent_increment = 0.8 * CArea::m_single_area_processing_length / num_steps; 426 | 427 | for(int i = 0; i &curve_list, const CAreaPocketParams ¶ms)const 455 | { 456 | CArea::m_processing_done = 0.0; 457 | 458 | double save_units = CArea::m_units; 459 | CArea::m_units = 1.0; 460 | std::list areas; 461 | m_split_processing_length = 50.0; // jump to 50 percent after split 462 | m_set_processing_length_in_split = true; 463 | Split(areas); 464 | m_set_processing_length_in_split = false; 465 | CArea::m_processing_done = m_split_processing_length; 466 | CArea::m_units = save_units; 467 | 468 | if(areas.size() == 0)return; 469 | 470 | double single_area_length = 50.0 / areas.size(); 471 | 472 | for(std::list::iterator It = areas.begin(); It != areas.end(); It++) 473 | { 474 | CArea::m_single_area_processing_length = single_area_length; 475 | CArea &ar = *It; 476 | ar.MakePocketToolpath(curve_list, params); 477 | } 478 | } 479 | 480 | void CArea::MakePocketToolpath(std::list &curve_list, const CAreaPocketParams ¶ms)const 481 | { 482 | double radians_angle = params.zig_angle * PI / 180; 483 | sin_angle_for_zigs = sin(-radians_angle); 484 | cos_angle_for_zigs = cos(-radians_angle); 485 | sin_minus_angle_for_zigs = sin(radians_angle); 486 | cos_minus_angle_for_zigs = cos(radians_angle); 487 | stepover_for_pocket = params.stepover; 488 | 489 | CArea a_offset = *this; 490 | double current_offset = params.tool_radius + params.extra_offset; 491 | 492 | a_offset.Offset(current_offset); 493 | 494 | if(params.mode == ZigZagPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) 495 | { 496 | curve_list_for_zigs = &curve_list; 497 | zigzag(a_offset); 498 | } 499 | else if(params.mode == SpiralPocketMode) 500 | { 501 | std::list m_areas; 502 | a_offset.Split(m_areas); 503 | if(CArea::m_please_abort)return; 504 | if(m_areas.size() == 0) 505 | { 506 | CArea::m_processing_done += CArea::m_single_area_processing_length; 507 | return; 508 | } 509 | 510 | CArea::m_single_area_processing_length /= m_areas.size(); 511 | 512 | for(std::list::iterator It = m_areas.begin(); It != m_areas.end(); It++) 513 | { 514 | CArea &a2 = *It; 515 | a2.MakeOnePocketCurve(curve_list, params); 516 | } 517 | } 518 | 519 | if(params.mode == SingleOffsetPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) 520 | { 521 | // add the single offset too 522 | for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) 523 | { 524 | CCurve& curve = *It; 525 | curve_list.push_back(curve); 526 | } 527 | } 528 | } 529 | 530 | void CArea::Split(std::list &m_areas)const 531 | { 532 | if(HolesLinked()) 533 | { 534 | for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) 535 | { 536 | const CCurve& curve = *It; 537 | m_areas.push_back(CArea()); 538 | m_areas.back().m_curves.push_back(curve); 539 | } 540 | } 541 | else 542 | { 543 | CArea a = *this; 544 | a.Reorder(); 545 | 546 | if(CArea::m_please_abort)return; 547 | 548 | for(std::list::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) 549 | { 550 | const CCurve& curve = *It; 551 | if(curve.IsClockwise()) 552 | { 553 | if(m_areas.size() > 0) 554 | m_areas.back().m_curves.push_back(curve); 555 | } 556 | else 557 | { 558 | m_areas.push_back(CArea()); 559 | m_areas.back().m_curves.push_back(curve); 560 | } 561 | } 562 | } 563 | } 564 | 565 | double CArea::GetArea(bool always_add)const 566 | { 567 | // returns the area of the area 568 | double area = 0.0; 569 | for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) 570 | { 571 | const CCurve& curve = *It; 572 | double a = curve.GetArea(); 573 | if(always_add)area += fabs(a); 574 | else area += a; 575 | } 576 | return area; 577 | } 578 | 579 | eOverlapType GetOverlapType(const CCurve& c1, const CCurve& c2) 580 | { 581 | CArea a1; 582 | a1.m_curves.push_back(c1); 583 | CArea a2; 584 | a2.m_curves.push_back(c2); 585 | 586 | return GetOverlapType(a1, a2); 587 | } 588 | 589 | eOverlapType GetOverlapType(const CArea& a1, const CArea& a2) 590 | { 591 | CArea A1(a1); 592 | 593 | A1.Subtract(a2); 594 | if(A1.m_curves.size() == 0) 595 | { 596 | return eInside; 597 | } 598 | 599 | CArea A2(a2); 600 | A2.Subtract(a1); 601 | if(A2.m_curves.size() == 0) 602 | { 603 | return eOutside; 604 | } 605 | 606 | A1 = a1; 607 | A1.Intersect(a2); 608 | if(A1.m_curves.size() == 0) 609 | { 610 | return eSiblings; 611 | } 612 | 613 | return eCrossing; 614 | } 615 | 616 | bool IsInside(const Point& p, const CCurve& c) 617 | { 618 | CArea a; 619 | a.m_curves.push_back(c); 620 | return IsInside(p, a); 621 | } 622 | 623 | bool IsInside(const Point& p, const CArea& a) 624 | { 625 | CArea a2; 626 | CCurve c; 627 | c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y - 0.01))); 628 | c.m_vertices.push_back(CVertex(Point(p.x + 0.01, p.y - 0.01))); 629 | c.m_vertices.push_back(CVertex(Point(p.x + 0.01, p.y + 0.01))); 630 | c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y + 0.01))); 631 | c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y - 0.01))); 632 | a2.m_curves.push_back(c); 633 | a2.Intersect(a); 634 | if(fabs(a2.GetArea()) < 0.0004)return false; 635 | return true; 636 | } 637 | 638 | void CArea::SpanIntersections(const Span& span, std::list &pts)const 639 | { 640 | // this returns all the intersections of this area with the given span, ordered along the span 641 | 642 | // get all points where this area's curves intersect the span 643 | std::list pts2; 644 | for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) 645 | { 646 | const CCurve &c = *It; 647 | c.SpanIntersections(span, pts2); 648 | } 649 | 650 | // order them along the span 651 | std::multimap ordered_points; 652 | for(std::list::iterator It = pts2.begin(); It != pts2.end(); It++) 653 | { 654 | Point &p = *It; 655 | double t; 656 | if(span.On(p, &t)) 657 | { 658 | ordered_points.insert(std::make_pair(t, p)); 659 | } 660 | } 661 | 662 | // add them to the given list of points 663 | for(std::multimap::iterator It = ordered_points.begin(); It != ordered_points.end(); It++) 664 | { 665 | Point p = It->second; 666 | pts.push_back(p); 667 | } 668 | } 669 | 670 | void CArea::CurveIntersections(const CCurve& curve, std::list &pts)const 671 | { 672 | // this returns all the intersections of this area with the given curve, ordered along the curve 673 | std::list spans; 674 | curve.GetSpans(spans); 675 | for(std::list::iterator It = spans.begin(); It != spans.end(); It++) 676 | { 677 | Span& span = *It; 678 | std::list pts2; 679 | SpanIntersections(span, pts2); 680 | for(std::list::iterator It = pts2.begin(); It != pts2.end(); It++) 681 | { 682 | Point &pt = *It; 683 | if(pts.size() == 0) 684 | { 685 | pts.push_back(pt); 686 | } 687 | else 688 | { 689 | if(pt != pts.back())pts.push_back(pt); 690 | } 691 | } 692 | } 693 | } 694 | 695 | class ThickLine 696 | { 697 | public: 698 | CArea m_area; 699 | CCurve m_curve; 700 | 701 | ThickLine(const CCurve& curve) 702 | { 703 | m_curve = curve; 704 | m_area.append(curve); 705 | m_area.Thicken(0.001); 706 | } 707 | }; 708 | 709 | void CArea::InsideCurves(const CCurve& curve, std::list &curves_inside)const 710 | { 711 | //1. find the intersectionpoints between these two curves. 712 | std::list pts; 713 | CurveIntersections(curve, pts); 714 | 715 | //2.seperate curve2 in multiple curves between these intersections. 716 | std::list separate_curves; 717 | curve.ExtractSeparateCurves(pts, separate_curves); 718 | 719 | //3. if the midpoint of a seperate curve lies in a1, then we return it. 720 | for(std::list::iterator It = separate_curves.begin(); It != separate_curves.end(); It++) 721 | { 722 | CCurve &curve = *It; 723 | double length = curve.Perim(); 724 | Point mid_point = curve.PerimToPoint(length * 0.5); 725 | if(IsInside(mid_point, *this))curves_inside.push_back(curve); 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /Area.h: -------------------------------------------------------------------------------- 1 | // Area.h 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #ifndef AREA_HEADER 6 | #define AREA_HEADER 7 | 8 | #include "Curve.h" 9 | 10 | enum PocketMode 11 | { 12 | SpiralPocketMode, 13 | ZigZagPocketMode, 14 | SingleOffsetPocketMode, 15 | ZigZagThenSingleOffsetPocketMode, 16 | }; 17 | 18 | struct CAreaPocketParams 19 | { 20 | double tool_radius; 21 | double extra_offset; 22 | double stepover; 23 | bool from_center; 24 | PocketMode mode; 25 | double zig_angle; 26 | bool only_cut_first_offset; 27 | CAreaPocketParams(double Tool_radius, double Extra_offset, double Stepover, bool From_center, PocketMode Mode, double Zig_angle) 28 | { 29 | tool_radius = Tool_radius; 30 | extra_offset = Extra_offset; 31 | stepover = Stepover; 32 | from_center = From_center; 33 | mode = Mode; 34 | zig_angle = Zig_angle; 35 | } 36 | }; 37 | 38 | class CArea 39 | { 40 | public: 41 | std::list m_curves; 42 | static double m_accuracy; 43 | static double m_units; // 1.0 for mm, 25.4 for inches. All points are multiplied by this before going to the engine 44 | static bool m_fit_arcs; 45 | static double m_processing_done; // 0.0 to 100.0, set inside MakeOnePocketCurve 46 | static double m_single_area_processing_length; 47 | static double m_after_MakeOffsets_length; 48 | static double m_MakeOffsets_increment; 49 | static double m_split_processing_length; 50 | static bool m_set_processing_length_in_split; 51 | static bool m_please_abort; // the user sets this from another thread, to tell MakeOnePocketCurve to finish with no result. 52 | 53 | void append(const CCurve& curve); 54 | void Subtract(const CArea& a2); 55 | void Intersect(const CArea& a2); 56 | void Union(const CArea& a2); 57 | void Xor(const CArea& a2); 58 | void Offset(double inwards_value); 59 | void Thicken(double value); 60 | void FitArcs(); 61 | unsigned int num_curves(){return m_curves.size();} 62 | Point NearestPoint(const Point& p)const; 63 | void GetBox(CBox2D &box); 64 | void Reorder(); 65 | void MakePocketToolpath(std::list &toolpath, const CAreaPocketParams ¶ms)const; 66 | void SplitAndMakePocketToolpath(std::list &toolpath, const CAreaPocketParams ¶ms)const; 67 | void MakeOnePocketCurve(std::list &curve_list, const CAreaPocketParams ¶ms)const; 68 | static bool HolesLinked(); 69 | void Split(std::list &m_areas)const; 70 | double GetArea(bool always_add = false)const; 71 | void SpanIntersections(const Span& span, std::list &pts)const; 72 | void CurveIntersections(const CCurve& curve, std::list &pts)const; 73 | void InsideCurves(const CCurve& curve, std::list &curves_inside)const; 74 | }; 75 | 76 | enum eOverlapType 77 | { 78 | eOutside, 79 | eInside, 80 | eSiblings, 81 | eCrossing, 82 | }; 83 | 84 | eOverlapType GetOverlapType(const CCurve& c1, const CCurve& c2); 85 | eOverlapType GetOverlapType(const CArea& a1, const CArea& a2); 86 | bool IsInside(const Point& p, const CCurve& c); 87 | bool IsInside(const Point& p, const CArea& a); 88 | 89 | #endif // #define AREA_HEADER 90 | -------------------------------------------------------------------------------- /AreaClipper.cpp: -------------------------------------------------------------------------------- 1 | // AreaClipper.cpp 2 | 3 | // implements CArea methods using Angus Johnson's "Clipper" 4 | 5 | #include "Area.h" 6 | #include "clipper.hpp" 7 | using namespace ClipperLib; 8 | 9 | #define TPolygon Path 10 | #define TPolyPolygon Paths 11 | 12 | bool CArea::HolesLinked(){ return false; } 13 | 14 | //static const double PI = 3.1415926535897932; 15 | static double Clipper4Factor = 10000.0; 16 | 17 | class DoubleAreaPoint 18 | { 19 | public: 20 | double X, Y; 21 | 22 | DoubleAreaPoint(double x, double y){X = x; Y = y;} 23 | DoubleAreaPoint(const IntPoint& p){X = (double)(p.X) / Clipper4Factor; Y = (double)(p.Y) / Clipper4Factor;} 24 | IntPoint int_point(){return IntPoint((long64)(X * Clipper4Factor), (long64)(Y * Clipper4Factor));} 25 | }; 26 | 27 | static std::list pts_for_AddVertex; 28 | 29 | static void AddPoint(const DoubleAreaPoint& p) 30 | { 31 | pts_for_AddVertex.push_back(p); 32 | } 33 | 34 | static void AddVertex(const CVertex& vertex, const CVertex* prev_vertex) 35 | { 36 | if(vertex.m_type == 0 || prev_vertex == NULL) 37 | { 38 | AddPoint(DoubleAreaPoint(vertex.m_p.x * CArea::m_units, vertex.m_p.y * CArea::m_units)); 39 | } 40 | else 41 | { 42 | if(vertex.m_p != prev_vertex->m_p) 43 | { 44 | double phi,dphi,dx,dy; 45 | int Segments; 46 | int i; 47 | double ang1,ang2,phit; 48 | 49 | dx = (prev_vertex->m_p.x - vertex.m_c.x) * CArea::m_units; 50 | dy = (prev_vertex->m_p.y - vertex.m_c.y) * CArea::m_units; 51 | 52 | ang1=atan2(dy,dx); 53 | if (ang1<0) ang1+=2.0*PI; 54 | dx = (vertex.m_p.x - vertex.m_c.x) * CArea::m_units; 55 | dy = (vertex.m_p.y - vertex.m_c.y) * CArea::m_units; 56 | ang2=atan2(dy,dx); 57 | if (ang2<0) ang2+=2.0*PI; 58 | 59 | if (vertex.m_type == -1) 60 | { //clockwise 61 | if (ang2 > ang1) 62 | phit=2.0*PI-ang2+ ang1; 63 | else 64 | phit=ang1-ang2; 65 | } 66 | else 67 | { //counter_clockwise 68 | if (ang1 > ang2) 69 | phit=-(2.0*PI-ang1+ ang2); 70 | else 71 | phit=-(ang2-ang1); 72 | } 73 | 74 | //what is the delta phi to get an accurancy of aber 75 | double radius = sqrt(dx*dx + dy*dy); 76 | dphi=2*acos((radius-CArea::m_accuracy)/radius); 77 | 78 | //set the number of segments 79 | if (phit > 0) 80 | Segments=(int)ceil(phit/dphi); 81 | else 82 | Segments=(int)ceil(-phit/dphi); 83 | 84 | if (Segments < 1) 85 | Segments=1; 86 | if (Segments > 100) 87 | Segments=100; 88 | 89 | dphi=phit/(Segments); 90 | 91 | double px = prev_vertex->m_p.x * CArea::m_units; 92 | double py = prev_vertex->m_p.y * CArea::m_units; 93 | 94 | for (i=1; i<=Segments; i++) 95 | { 96 | dx = px - vertex.m_c.x * CArea::m_units; 97 | dy = py - vertex.m_c.y * CArea::m_units; 98 | phi=atan2(dy,dx); 99 | 100 | double nx = vertex.m_c.x * CArea::m_units + radius * cos(phi-dphi); 101 | double ny = vertex.m_c.y * CArea::m_units + radius * sin(phi-dphi); 102 | 103 | AddPoint(DoubleAreaPoint(nx, ny)); 104 | 105 | px = nx; 106 | py = ny; 107 | } 108 | } 109 | } 110 | } 111 | 112 | static bool IsPolygonClockwise(const TPolygon& p) 113 | { 114 | #if 1 115 | double area = 0.0; 116 | unsigned int s = p.size(); 117 | for(unsigned int i = 0; i 0.0; 129 | #else 130 | return IsClockwise(p); 131 | #endif 132 | } 133 | 134 | static void MakeLoop(const DoubleAreaPoint &pt0, const DoubleAreaPoint &pt1, const DoubleAreaPoint &pt2, double radius) 135 | { 136 | Point p0(pt0.X, pt0.Y); 137 | Point p1(pt1.X, pt1.Y); 138 | Point p2(pt2.X, pt2.Y); 139 | Point forward0 = p1 - p0; 140 | Point right0(forward0.y, -forward0.x); 141 | right0.normalize(); 142 | Point forward1 = p2 - p1; 143 | Point right1(forward1.y, -forward1.x); 144 | right1.normalize(); 145 | 146 | int arc_dir = (radius > 0) ? 1 : -1; 147 | 148 | CVertex v0(0, p1 + right0 * radius, Point(0, 0)); 149 | CVertex v1(arc_dir, p1 + right1 * radius, p1); 150 | CVertex v2(0, p2 + right1 * radius, Point(0, 0)); 151 | 152 | double save_units = CArea::m_units; 153 | CArea::m_units = 1.0; 154 | 155 | AddVertex(v1, &v0); 156 | AddVertex(v2, &v1); 157 | 158 | CArea::m_units = save_units; 159 | } 160 | 161 | static void OffsetWithLoops(const TPolyPolygon &pp, TPolyPolygon &pp_new, double inwards_value) 162 | { 163 | Clipper c; 164 | 165 | bool inwards = (inwards_value > 0); 166 | bool reverse = false; 167 | double radius = -fabs(inwards_value); 168 | 169 | if(inwards) 170 | { 171 | // add a large square on the outside, to be removed later 172 | TPolygon p; 173 | p.push_back(DoubleAreaPoint(-10000.0, -10000.0).int_point()); 174 | p.push_back(DoubleAreaPoint(-10000.0, 10000.0).int_point()); 175 | p.push_back(DoubleAreaPoint(10000.0, 10000.0).int_point()); 176 | p.push_back(DoubleAreaPoint(10000.0, -10000.0).int_point()); 177 | c.AddPath(p, ptSubject, true); 178 | } 179 | else 180 | { 181 | reverse = true; 182 | } 183 | 184 | for(unsigned int i = 0; i < pp.size(); i++) 185 | { 186 | const TPolygon& p = pp[i]; 187 | 188 | pts_for_AddVertex.clear(); 189 | 190 | if(p.size() > 2) 191 | { 192 | if(reverse) 193 | { 194 | for(unsigned int j = p.size()-1; j > 1; j--)MakeLoop(p[j], p[j-1], p[j-2], radius); 195 | MakeLoop(p[1], p[0], p[p.size()-1], radius); 196 | MakeLoop(p[0], p[p.size()-1], p[p.size()-2], radius); 197 | } 198 | else 199 | { 200 | MakeLoop(p[p.size()-2], p[p.size()-1], p[0], radius); 201 | MakeLoop(p[p.size()-1], p[0], p[1], radius); 202 | for(unsigned int j = 2; j < p.size(); j++)MakeLoop(p[j-2], p[j-1], p[j], radius); 203 | } 204 | 205 | TPolygon loopy_polygon; 206 | loopy_polygon.reserve(pts_for_AddVertex.size()); 207 | for(std::list::iterator It = pts_for_AddVertex.begin(); It != pts_for_AddVertex.end(); It++) 208 | { 209 | loopy_polygon.push_back(It->int_point()); 210 | } 211 | c.AddPath(loopy_polygon, ptSubject, true); 212 | pts_for_AddVertex.clear(); 213 | } 214 | } 215 | 216 | //c.ForceOrientation(false); 217 | c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); 218 | 219 | if(inwards) 220 | { 221 | // remove the large square 222 | if(pp_new.size() > 0) 223 | { 224 | pp_new.erase(pp_new.begin()); 225 | } 226 | } 227 | else 228 | { 229 | // reverse all the resulting polygons 230 | TPolyPolygon copy = pp_new; 231 | pp_new.clear(); 232 | pp_new.resize(copy.size()); 233 | for(unsigned int i = 0; i < copy.size(); i++) 234 | { 235 | const TPolygon& p = copy[i]; 236 | TPolygon p_new; 237 | p_new.resize(p.size()); 238 | int size_minus_one = p.size() - 1; 239 | for(unsigned int j = 0; j < p.size(); j++)p_new[j] = p[size_minus_one - j]; 240 | pp_new[i] = p_new; 241 | } 242 | } 243 | } 244 | 245 | static void MakeObround(const Point &pt0, const CVertex &vt1, double radius) 246 | { 247 | Span span(pt0, vt1); 248 | Point forward0 = span.GetVector(0.0); 249 | Point forward1 = span.GetVector(1.0); 250 | Point right0(forward0.y, -forward0.x); 251 | Point right1(forward1.y, -forward1.x); 252 | right0.normalize(); 253 | right1.normalize(); 254 | 255 | CVertex v0(pt0 + right0 * radius); 256 | CVertex v1(vt1.m_type, vt1.m_p + right1 * radius, vt1.m_c); 257 | CVertex v2(1, vt1.m_p + right1 * -radius, vt1.m_p); 258 | CVertex v3(-vt1.m_type, pt0 + right0 * -radius, vt1.m_c); 259 | CVertex v4(1, pt0 + right0 * radius, pt0); 260 | 261 | double save_units = CArea::m_units; 262 | CArea::m_units = 1.0; 263 | 264 | AddVertex(v0, NULL); 265 | AddVertex(v1, &v0); 266 | AddVertex(v2, &v1); 267 | AddVertex(v3, &v2); 268 | AddVertex(v4, &v3); 269 | 270 | CArea::m_units = save_units; 271 | } 272 | 273 | static void OffsetSpansWithObrounds(const CArea& area, TPolyPolygon &pp_new, double radius) 274 | { 275 | Clipper c; 276 | 277 | 278 | for(std::list::const_iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) 279 | { 280 | pts_for_AddVertex.clear(); 281 | const CCurve& curve = *It; 282 | const CVertex* prev_vertex = NULL; 283 | for(std::list::const_iterator It2 = curve.m_vertices.begin(); It2 != curve.m_vertices.end(); It2++) 284 | { 285 | const CVertex& vertex = *It2; 286 | if(prev_vertex) 287 | { 288 | MakeObround(prev_vertex->m_p, vertex, radius); 289 | 290 | TPolygon loopy_polygon; 291 | loopy_polygon.reserve(pts_for_AddVertex.size()); 292 | for(std::list::iterator It = pts_for_AddVertex.begin(); It != pts_for_AddVertex.end(); It++) 293 | { 294 | loopy_polygon.push_back(It->int_point()); 295 | } 296 | c.AddPath(loopy_polygon, ptSubject, true); 297 | pts_for_AddVertex.clear(); 298 | } 299 | prev_vertex = &vertex; 300 | } 301 | } 302 | 303 | pp_new.clear(); 304 | c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); 305 | 306 | // reverse all the resulting polygons 307 | TPolyPolygon copy = pp_new; 308 | pp_new.clear(); 309 | pp_new.resize(copy.size()); 310 | for(unsigned int i = 0; i < copy.size(); i++) 311 | { 312 | const TPolygon& p = copy[i]; 313 | TPolygon p_new; 314 | p_new.resize(p.size()); 315 | int size_minus_one = p.size() - 1; 316 | for(unsigned int j = 0; j < p.size(); j++)p_new[j] = p[size_minus_one - j]; 317 | pp_new[i] = p_new; 318 | } 319 | } 320 | 321 | static void MakePolyPoly( const CArea& area, TPolyPolygon &pp, bool reverse = true ){ 322 | pp.clear(); 323 | 324 | for(std::list::const_iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) 325 | { 326 | pts_for_AddVertex.clear(); 327 | const CCurve& curve = *It; 328 | const CVertex* prev_vertex = NULL; 329 | for(std::list::const_iterator It2 = curve.m_vertices.begin(); It2 != curve.m_vertices.end(); It2++) 330 | { 331 | const CVertex& vertex = *It2; 332 | if(prev_vertex)AddVertex(vertex, prev_vertex); 333 | prev_vertex = &vertex; 334 | } 335 | 336 | TPolygon p; 337 | p.resize(pts_for_AddVertex.size()); 338 | if(reverse) 339 | { 340 | unsigned int i = pts_for_AddVertex.size() - 1;// clipper wants them the opposite way to CArea 341 | for(std::list::iterator It = pts_for_AddVertex.begin(); It != pts_for_AddVertex.end(); It++, i--) 342 | { 343 | p[i] = It->int_point(); 344 | } 345 | } 346 | else 347 | { 348 | unsigned int i = 0; 349 | for(std::list::iterator It = pts_for_AddVertex.begin(); It != pts_for_AddVertex.end(); It++, i++) 350 | { 351 | p[i] = It->int_point(); 352 | } 353 | } 354 | 355 | pp.push_back(p); 356 | } 357 | } 358 | 359 | static void SetFromResult( CCurve& curve, const TPolygon& p, bool reverse = true ) 360 | { 361 | for(unsigned int j = 0; j < p.size(); j++) 362 | { 363 | const IntPoint &pt = p[j]; 364 | DoubleAreaPoint dp(pt); 365 | CVertex vertex(0, Point(dp.X / CArea::m_units, dp.Y / CArea::m_units), Point(0.0, 0.0)); 366 | if(reverse)curve.m_vertices.push_front(vertex); 367 | else curve.m_vertices.push_back(vertex); 368 | } 369 | // make a copy of the first point at the end 370 | if(reverse)curve.m_vertices.push_front(curve.m_vertices.back()); 371 | else curve.m_vertices.push_back(curve.m_vertices.front()); 372 | 373 | if(CArea::m_fit_arcs)curve.FitArcs(); 374 | } 375 | 376 | static void SetFromResult( CArea& area, const TPolyPolygon& pp, bool reverse = true ) 377 | { 378 | // delete existing geometry 379 | area.m_curves.clear(); 380 | 381 | for(unsigned int i = 0; i < pp.size(); i++) 382 | { 383 | const TPolygon& p = pp[i]; 384 | 385 | area.m_curves.push_back(CCurve()); 386 | CCurve &curve = area.m_curves.back(); 387 | SetFromResult(curve, p, reverse); 388 | } 389 | } 390 | 391 | void CArea::Subtract(const CArea& a2) 392 | { 393 | Clipper c; 394 | TPolyPolygon pp1, pp2; 395 | MakePolyPoly(*this, pp1); 396 | MakePolyPoly(a2, pp2); 397 | c.AddPaths(pp1, ptSubject, true); 398 | c.AddPaths(pp2, ptClip, true); 399 | TPolyPolygon solution; 400 | c.Execute(ctDifference, solution); 401 | SetFromResult(*this, solution); 402 | } 403 | 404 | void CArea::Intersect(const CArea& a2) 405 | { 406 | Clipper c; 407 | TPolyPolygon pp1, pp2; 408 | MakePolyPoly(*this, pp1); 409 | MakePolyPoly(a2, pp2); 410 | c.AddPaths(pp1, ptSubject, true); 411 | c.AddPaths(pp2, ptClip, true); 412 | TPolyPolygon solution; 413 | c.Execute(ctIntersection, solution); 414 | SetFromResult(*this, solution); 415 | } 416 | 417 | void CArea::Union(const CArea& a2) 418 | { 419 | Clipper c; 420 | TPolyPolygon pp1, pp2; 421 | MakePolyPoly(*this, pp1); 422 | MakePolyPoly(a2, pp2); 423 | c.AddPaths(pp1, ptSubject, true); 424 | c.AddPaths(pp2, ptClip, true); 425 | TPolyPolygon solution; 426 | c.Execute(ctUnion, solution); 427 | SetFromResult(*this, solution); 428 | } 429 | 430 | void CArea::Xor(const CArea& a2) 431 | { 432 | Clipper c; 433 | TPolyPolygon pp1, pp2; 434 | MakePolyPoly(*this, pp1); 435 | MakePolyPoly(a2, pp2); 436 | c.AddPaths(pp1, ptSubject, true); 437 | c.AddPaths(pp2, ptClip, true); 438 | TPolyPolygon solution; 439 | c.Execute(ctXor, solution); 440 | SetFromResult(*this, solution); 441 | } 442 | 443 | void CArea::Offset(double inwards_value) 444 | { 445 | TPolyPolygon pp, pp2; 446 | MakePolyPoly(*this, pp, false); 447 | OffsetWithLoops(pp, pp2, inwards_value * m_units); 448 | SetFromResult(*this, pp2, false); 449 | this->Reorder(); 450 | } 451 | 452 | void CArea::Thicken(double value) 453 | { 454 | TPolyPolygon pp; 455 | OffsetSpansWithObrounds(*this, pp, value * m_units); 456 | SetFromResult(*this, pp, false); 457 | this->Reorder(); 458 | } 459 | 460 | void UnFitArcs(CCurve &curve) 461 | { 462 | pts_for_AddVertex.clear(); 463 | const CVertex* prev_vertex = NULL; 464 | for(std::list::const_iterator It2 = curve.m_vertices.begin(); It2 != curve.m_vertices.end(); It2++) 465 | { 466 | const CVertex& vertex = *It2; 467 | AddVertex(vertex, prev_vertex); 468 | prev_vertex = &vertex; 469 | } 470 | 471 | curve.m_vertices.clear(); 472 | 473 | for(std::list::iterator It = pts_for_AddVertex.begin(); It != pts_for_AddVertex.end(); It++) 474 | { 475 | DoubleAreaPoint &pt = *It; 476 | CVertex vertex(0, Point(pt.X / CArea::m_units, pt.Y / CArea::m_units), Point(0.0, 0.0)); 477 | curve.m_vertices.push_back(vertex); 478 | } 479 | } -------------------------------------------------------------------------------- /AreaDxf.cpp: -------------------------------------------------------------------------------- 1 | // AreaDxf.cpp 2 | // Copyright (c) 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #include "AreaDxf.h" 6 | #include "Area.h" 7 | 8 | AreaDxfRead::AreaDxfRead(CArea* area, const char* filepath):CDxfRead(filepath), m_area(area){} 9 | 10 | void AreaDxfRead::StartCurveIfNecessary(const double* s) 11 | { 12 | Point ps(s); 13 | if((m_area->m_curves.size() == 0) || (m_area->m_curves.back().m_vertices.size() == 0) || (m_area->m_curves.back().m_vertices.back().m_p != ps)) 14 | { 15 | // start a new curve 16 | m_area->m_curves.push_back(CCurve()); 17 | m_area->m_curves.back().m_vertices.push_back(ps); 18 | } 19 | } 20 | 21 | void AreaDxfRead::OnReadLine(const double* s, const double* e) 22 | { 23 | StartCurveIfNecessary(s); 24 | m_area->m_curves.back().m_vertices.push_back(Point(e)); 25 | } 26 | 27 | void AreaDxfRead::OnReadArc(const double* s, const double* e, const double* c, bool dir) 28 | { 29 | StartCurveIfNecessary(s); 30 | m_area->m_curves.back().m_vertices.push_back(CVertex(dir?1:0, Point(e), Point(c))); 31 | } 32 | -------------------------------------------------------------------------------- /AreaDxf.h: -------------------------------------------------------------------------------- 1 | // AreaDxf.h 2 | // Copyright (c) 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | 7 | #include "dxf.h" 8 | 9 | class CSketch; 10 | class CArea; 11 | class CCurve; 12 | 13 | class AreaDxfRead : public CDxfRead{ 14 | void StartCurveIfNecessary(const double* s); 15 | 16 | public: 17 | CArea* m_area; 18 | AreaDxfRead(CArea* area, const char* filepath); 19 | 20 | // AreaDxfRead's virtual functions 21 | void OnReadLine(const double* s, const double* e); 22 | void OnReadArc(const double* s, const double* e, const double* c, bool dir); 23 | }; 24 | -------------------------------------------------------------------------------- /AreaOrderer.cpp: -------------------------------------------------------------------------------- 1 | // AreaOrderer.cpp 2 | // Copyright (c) 2010, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #include "AreaOrderer.h" 6 | #include "Area.h" 7 | 8 | CAreaOrderer* CInnerCurves::area_orderer = NULL; 9 | 10 | CInnerCurves::CInnerCurves(CInnerCurves* pOuter, const CCurve* curve) 11 | { 12 | m_pOuter = pOuter; 13 | m_curve = curve; 14 | m_unite_area = NULL; 15 | } 16 | 17 | CInnerCurves::~CInnerCurves() 18 | { 19 | delete m_unite_area; 20 | } 21 | 22 | void CInnerCurves::Insert(const CCurve* pcurve) 23 | { 24 | std::list outside_of_these; 25 | std::list crossing_these; 26 | 27 | // check all inner curves 28 | for(std::set::iterator It = m_inner_curves.begin(); It != m_inner_curves.end(); It++) 29 | { 30 | CInnerCurves* c = *It; 31 | 32 | switch(GetOverlapType(*pcurve, *(c->m_curve))) 33 | { 34 | case eOutside: 35 | outside_of_these.push_back(c); 36 | break; 37 | 38 | case eInside: 39 | // insert in this inner curve 40 | c->Insert(pcurve); 41 | return; 42 | 43 | case eSiblings: 44 | break; 45 | 46 | case eCrossing: 47 | crossing_these.push_back(c); 48 | break; 49 | } 50 | } 51 | 52 | // add as a new inner 53 | CInnerCurves* new_item = new CInnerCurves(this, pcurve); 54 | this->m_inner_curves.insert(new_item); 55 | 56 | for(std::list::iterator It = outside_of_these.begin(); It != outside_of_these.end(); It++) 57 | { 58 | // move items 59 | CInnerCurves* c = *It; 60 | c->m_pOuter = new_item; 61 | new_item->m_inner_curves.insert(c); 62 | this->m_inner_curves.erase(c); 63 | } 64 | 65 | for(std::list::iterator It = crossing_these.begin(); It != crossing_these.end(); It++) 66 | { 67 | // unite these 68 | CInnerCurves* c = *It; 69 | new_item->Unite(c); 70 | this->m_inner_curves.erase(c); 71 | } 72 | } 73 | 74 | void CInnerCurves::GetArea(CArea &area, bool outside, bool use_curve)const 75 | { 76 | if(use_curve && m_curve) 77 | { 78 | area.m_curves.push_back(*m_curve); 79 | outside = !outside; 80 | } 81 | 82 | std::list do_after; 83 | 84 | for(std::set::const_iterator It = m_inner_curves.begin(); It != m_inner_curves.end(); It++) 85 | { 86 | const CInnerCurves* c = *It; 87 | area.m_curves.push_back(*c->m_curve); 88 | if(!outside)area.m_curves.back().Reverse(); 89 | 90 | if(outside)c->GetArea(area, !outside, false); 91 | else do_after.push_back(c); 92 | } 93 | 94 | for(std::list::iterator It = do_after.begin(); It != do_after.end(); It++) 95 | { 96 | const CInnerCurves* c = *It; 97 | c->GetArea(area, !outside, false); 98 | } 99 | } 100 | 101 | void CInnerCurves::Unite(const CInnerCurves* c) 102 | { 103 | // unite all the curves in c, with this one 104 | CArea* new_area = new CArea(); 105 | new_area->m_curves.push_back(*m_curve); 106 | delete m_unite_area; 107 | m_unite_area = new_area; 108 | 109 | CArea a2; 110 | c->GetArea(a2); 111 | 112 | m_unite_area->Union(a2); 113 | m_unite_area->Reorder(); 114 | for(std::list::iterator It = m_unite_area->m_curves.begin(); It != m_unite_area->m_curves.end(); It++) 115 | { 116 | CCurve &curve = *It; 117 | if(It == m_unite_area->m_curves.begin()) 118 | m_curve = &curve; 119 | else 120 | { 121 | if(curve.IsClockwise())curve.Reverse(); 122 | Insert(&curve); 123 | } 124 | } 125 | } 126 | 127 | CAreaOrderer::CAreaOrderer() 128 | { 129 | m_top_level = new CInnerCurves(NULL, NULL); 130 | } 131 | 132 | void CAreaOrderer::Insert(CCurve* pcurve) 133 | { 134 | CInnerCurves::area_orderer = this; 135 | 136 | // make them all anti-clockwise as they come in 137 | if(pcurve->IsClockwise())pcurve->Reverse(); 138 | 139 | m_top_level->Insert(pcurve); 140 | } 141 | 142 | CArea CAreaOrderer::ResultArea()const 143 | { 144 | CArea a; 145 | 146 | if(m_top_level) 147 | { 148 | m_top_level->GetArea(a); 149 | } 150 | 151 | return a; 152 | } 153 | 154 | -------------------------------------------------------------------------------- /AreaOrderer.h: -------------------------------------------------------------------------------- 1 | // AreaOrderer.h 2 | // Copyright (c) 2010, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | #include 7 | #include 8 | 9 | class CArea; 10 | class CCurve; 11 | 12 | class CAreaOrderer; 13 | 14 | class CInnerCurves 15 | { 16 | CInnerCurves* m_pOuter; 17 | const CCurve* m_curve; // always empty if top level 18 | std::set m_inner_curves; 19 | CArea *m_unite_area; // new curves made by uniting are stored here 20 | 21 | public: 22 | static CAreaOrderer* area_orderer; 23 | CInnerCurves(CInnerCurves* pOuter, const CCurve* curve); 24 | ~CInnerCurves(); 25 | 26 | void Insert(const CCurve* pcurve); 27 | void GetArea(CArea &area, bool outside = true, bool use_curve = true)const; 28 | void Unite(const CInnerCurves* c); 29 | }; 30 | 31 | class CAreaOrderer 32 | { 33 | public: 34 | CInnerCurves* m_top_level; 35 | 36 | CAreaOrderer(); 37 | 38 | void Insert(CCurve* pcurve); 39 | CArea ResultArea()const; 40 | }; -------------------------------------------------------------------------------- /AreaPocket.cpp: -------------------------------------------------------------------------------- 1 | // AreaPocket.cpp 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | // implements CArea::MakeOnePocketCurve 6 | 7 | #include "Area.h" 8 | 9 | #include 10 | #include 11 | 12 | static const CAreaPocketParams* pocket_params = NULL; 13 | 14 | class IslandAndOffset 15 | { 16 | public: 17 | const CCurve* island; 18 | CArea offset; 19 | std::list island_inners; 20 | std::list touching_offsets; 21 | 22 | IslandAndOffset(const CCurve* Island) 23 | { 24 | island = Island; 25 | 26 | offset.m_curves.push_back(*island); 27 | offset.m_curves.back().Reverse(); 28 | 29 | offset.Offset(-pocket_params->stepover); 30 | 31 | 32 | if(offset.m_curves.size() > 1) 33 | { 34 | for(std::list::iterator It = offset.m_curves.begin(); It != offset.m_curves.end(); It++) 35 | { 36 | if(It == offset.m_curves.begin())continue; 37 | island_inners.push_back(*It); 38 | island_inners.back().Reverse(); 39 | } 40 | offset.m_curves.resize(1); 41 | } 42 | } 43 | }; 44 | 45 | class CurveTree 46 | { 47 | static std::list to_do_list_for_MakeOffsets; 48 | void MakeOffsets2(); 49 | static std::list islands_added; 50 | 51 | public: 52 | Point point_on_parent; 53 | CCurve curve; 54 | std::list inners; 55 | std::list offset_islands; 56 | CurveTree(const CCurve &c) 57 | { 58 | curve = c; 59 | } 60 | ~CurveTree(){} 61 | 62 | void MakeOffsets(); 63 | }; 64 | std::list CurveTree::islands_added; 65 | 66 | class GetCurveItem 67 | { 68 | public: 69 | CurveTree* curve_tree; 70 | std::list::iterator EndIt; 71 | static std::list to_do_list; 72 | 73 | GetCurveItem(CurveTree* ct, std::list::iterator EIt):curve_tree(ct), EndIt(EIt){} 74 | 75 | void GetCurve(CCurve& output); 76 | CVertex& back(){std::list::iterator It = EndIt; It--; return *It;} 77 | }; 78 | 79 | std::list GetCurveItem::to_do_list; 80 | std::list CurveTree::to_do_list_for_MakeOffsets; 81 | 82 | void GetCurveItem::GetCurve(CCurve& output) 83 | { 84 | // walk around the curve adding spans to output until we get to an inner's point_on_parent 85 | // then add a line from the inner's point_on_parent to inner's start point, then GetCurve from inner 86 | 87 | // add start point 88 | if(CArea::m_please_abort)return; 89 | output.m_vertices.insert(this->EndIt, CVertex(curve_tree->curve.m_vertices.front())); 90 | 91 | std::list inners_to_visit; 92 | for(std::list::iterator It2 = curve_tree->inners.begin(); It2 != curve_tree->inners.end(); It2++) 93 | { 94 | inners_to_visit.push_back(*It2); 95 | } 96 | 97 | const CVertex* prev_vertex = NULL; 98 | 99 | for(std::list::iterator It = curve_tree->curve.m_vertices.begin(); It != curve_tree->curve.m_vertices.end(); It++) 100 | { 101 | const CVertex& vertex = *It; 102 | if(prev_vertex) 103 | { 104 | Span span(prev_vertex->m_p, vertex); 105 | 106 | // order inners on this span 107 | std::multimap ordered_inners; 108 | for(std::list::iterator It2 = inners_to_visit.begin(); It2 != inners_to_visit.end();) 109 | { 110 | CurveTree *inner = *It2; 111 | double t; 112 | if(span.On(inner->point_on_parent, &t)) 113 | { 114 | ordered_inners.insert(std::make_pair(t, inner)); 115 | It2 = inners_to_visit.erase(It2); 116 | } 117 | else 118 | { 119 | It2++; 120 | } 121 | if(CArea::m_please_abort)return; 122 | } 123 | 124 | if(CArea::m_please_abort)return; 125 | for(std::multimap::iterator It2 = ordered_inners.begin(); It2 != ordered_inners.end(); It2++) 126 | { 127 | CurveTree& inner = *(It2->second); 128 | if(inner.point_on_parent.dist(back().m_p) > 0.01/CArea::m_units) 129 | { 130 | output.m_vertices.insert(this->EndIt, CVertex(vertex.m_type, inner.point_on_parent, vertex.m_c)); 131 | } 132 | if(CArea::m_please_abort)return; 133 | 134 | // vertex add after GetCurve 135 | std::list::iterator VIt = output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); 136 | 137 | //inner.GetCurve(output); 138 | GetCurveItem::to_do_list.push_back(GetCurveItem(&inner, VIt)); 139 | } 140 | 141 | if(back().m_p != vertex.m_p)output.m_vertices.insert(this->EndIt, vertex); 142 | } 143 | prev_vertex = &vertex; 144 | } 145 | 146 | if(CArea::m_please_abort)return; 147 | for(std::list::iterator It2 = inners_to_visit.begin(); It2 != inners_to_visit.end(); It2++) 148 | { 149 | CurveTree &inner = *(*It2); 150 | if(inner.point_on_parent != back().m_p) 151 | { 152 | output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); 153 | } 154 | if(CArea::m_please_abort)return; 155 | 156 | // vertex add after GetCurve 157 | std::list::iterator VIt = output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); 158 | 159 | //inner.GetCurve(output); 160 | GetCurveItem::to_do_list.push_back(GetCurveItem(&inner, VIt)); 161 | 162 | } 163 | } 164 | 165 | class IslandAndOffsetLink 166 | { 167 | public: 168 | const IslandAndOffset* island_and_offset; 169 | CurveTree* add_to; 170 | IslandAndOffsetLink(const IslandAndOffset* i, CurveTree* a){island_and_offset = i; add_to = a;} 171 | }; 172 | 173 | static Point GetNearestPoint(CurveTree* curve_tree, std::list &islands_added, const CCurve &test_curve, CurveTree** best_curve_tree) 174 | { 175 | // find nearest point to test_curve, from curve and all the islands in 176 | double best_dist; 177 | Point best_point = curve_tree->curve.NearestPoint(test_curve, &best_dist); 178 | *best_curve_tree = curve_tree; 179 | for(std::list::iterator It = islands_added.begin(); It != islands_added.end(); It++) 180 | { 181 | CurveTree* island = *It; 182 | double dist; 183 | Point p = island->curve.NearestPoint(test_curve, &dist); 184 | if(dist < best_dist) 185 | { 186 | *best_curve_tree = island; 187 | best_point = p; 188 | best_dist = dist; 189 | } 190 | } 191 | 192 | return best_point; 193 | } 194 | 195 | void CurveTree::MakeOffsets2() 196 | { 197 | // make offsets 198 | 199 | if(CArea::m_please_abort)return; 200 | CArea smaller; 201 | smaller.m_curves.push_back(curve); 202 | smaller.Offset(pocket_params->stepover); 203 | 204 | if(CArea::m_please_abort)return; 205 | 206 | // test islands 207 | for(std::list::iterator It = offset_islands.begin(); It != offset_islands.end();) 208 | { 209 | const IslandAndOffset* island_and_offset = *It; 210 | 211 | if(GetOverlapType(island_and_offset->offset, smaller) == eInside) 212 | It++; // island is still inside 213 | else 214 | { 215 | inners.push_back(new CurveTree(*island_and_offset->island)); 216 | islands_added.push_back(inners.back()); 217 | inners.back()->point_on_parent = curve.NearestPoint(*island_and_offset->island); 218 | if(CArea::m_please_abort)return; 219 | Point island_point = island_and_offset->island->NearestPoint(inners.back()->point_on_parent); 220 | if(CArea::m_please_abort)return; 221 | inners.back()->curve.ChangeStart(island_point); 222 | if(CArea::m_please_abort)return; 223 | 224 | // add the island offset's inner curves 225 | for(std::list::const_iterator It2 = island_and_offset->island_inners.begin(); It2 != island_and_offset->island_inners.end(); It2++) 226 | { 227 | const CCurve& island_inner = *It2; 228 | inners.back()->inners.push_back(new CurveTree(island_inner)); 229 | inners.back()->inners.back()->point_on_parent = inners.back()->curve.NearestPoint(island_inner); 230 | if(CArea::m_please_abort)return; 231 | Point island_point = island_inner.NearestPoint(inners.back()->inners.back()->point_on_parent); 232 | if(CArea::m_please_abort)return; 233 | inners.back()->inners.back()->curve.ChangeStart(island_point); 234 | to_do_list_for_MakeOffsets.push_back(inners.back()->inners.back()); // do it later, in a while loop 235 | if(CArea::m_please_abort)return; 236 | } 237 | 238 | smaller.Subtract(island_and_offset->offset); 239 | 240 | std::set added; 241 | 242 | std::list touching_list; 243 | for(std::list::const_iterator It2 = island_and_offset->touching_offsets.begin(); It2 != island_and_offset->touching_offsets.end(); It2++) 244 | { 245 | const IslandAndOffset* touching = *It2; 246 | touching_list.push_back(IslandAndOffsetLink(touching, inners.back())); 247 | added.insert(touching); 248 | } 249 | 250 | while(touching_list.size() > 0) 251 | { 252 | IslandAndOffsetLink touching = touching_list.front(); 253 | touching_list.pop_front(); 254 | touching.add_to->inners.push_back(new CurveTree(*touching.island_and_offset->island)); 255 | islands_added.push_back(touching.add_to->inners.back()); 256 | touching.add_to->inners.back()->point_on_parent = touching.add_to->curve.NearestPoint(*touching.island_and_offset->island); 257 | Point island_point = touching.island_and_offset->island->NearestPoint(touching.add_to->inners.back()->point_on_parent); 258 | touching.add_to->inners.back()->curve.ChangeStart(island_point); 259 | smaller.Subtract(touching.island_and_offset->offset); 260 | 261 | // add the island offset's inner curves 262 | for(std::list::const_iterator It2 = touching.island_and_offset->island_inners.begin(); It2 != touching.island_and_offset->island_inners.end(); It2++) 263 | { 264 | const CCurve& island_inner = *It2; 265 | touching.add_to->inners.back()->inners.push_back(new CurveTree(island_inner)); 266 | touching.add_to->inners.back()->inners.back()->point_on_parent = touching.add_to->inners.back()->curve.NearestPoint(island_inner); 267 | if(CArea::m_please_abort)return; 268 | Point island_point = island_inner.NearestPoint(touching.add_to->inners.back()->inners.back()->point_on_parent); 269 | if(CArea::m_please_abort)return; 270 | touching.add_to->inners.back()->inners.back()->curve.ChangeStart(island_point); 271 | to_do_list_for_MakeOffsets.push_back(touching.add_to->inners.back()->inners.back()); // do it later, in a while loop 272 | if(CArea::m_please_abort)return; 273 | } 274 | 275 | for(std::list::const_iterator It2 = touching.island_and_offset->touching_offsets.begin(); It2 != touching.island_and_offset->touching_offsets.end(); It2++) 276 | { 277 | if(added.find(*It2)==added.end() && ((*It2) != island_and_offset)) 278 | { 279 | touching_list.push_back(IslandAndOffsetLink(*It2, touching.add_to->inners.back())); 280 | added.insert(*It2); 281 | } 282 | } 283 | } 284 | 285 | if(CArea::m_please_abort)return; 286 | It = offset_islands.erase(It); 287 | 288 | for(std::set::iterator It2 = added.begin(); It2 != added.end(); It2++) 289 | { 290 | const IslandAndOffset* i = *It2; 291 | offset_islands.remove(i); 292 | } 293 | 294 | if(offset_islands.size() == 0)break; 295 | It = offset_islands.begin(); 296 | } 297 | } 298 | 299 | CArea::m_processing_done += CArea::m_MakeOffsets_increment; 300 | if(CArea::m_processing_done > CArea::m_after_MakeOffsets_length)CArea::m_processing_done = CArea::m_after_MakeOffsets_length; 301 | 302 | std::list separate_areas; 303 | smaller.Split(separate_areas); 304 | if(CArea::m_please_abort)return; 305 | for(std::list::iterator It = separate_areas.begin(); It != separate_areas.end(); It++) 306 | { 307 | CArea& separate_area = *It; 308 | CCurve& first_curve = separate_area.m_curves.front(); 309 | 310 | CurveTree* nearest_curve_tree = NULL; 311 | Point near_point = GetNearestPoint(this, islands_added, first_curve, &nearest_curve_tree); 312 | 313 | nearest_curve_tree->inners.push_back(new CurveTree(first_curve)); 314 | 315 | for(std::list::iterator It = offset_islands.begin(); It != offset_islands.end();It++) 316 | { 317 | const IslandAndOffset* island_and_offset = *It; 318 | if(GetOverlapType(island_and_offset->offset, separate_area) == eInside) 319 | nearest_curve_tree->inners.back()->offset_islands.push_back(island_and_offset); 320 | if(CArea::m_please_abort)return; 321 | } 322 | 323 | nearest_curve_tree->inners.back()->point_on_parent = near_point; 324 | 325 | if(CArea::m_please_abort)return; 326 | Point first_curve_point = first_curve.NearestPoint(nearest_curve_tree->inners.back()->point_on_parent); 327 | if(CArea::m_please_abort)return; 328 | nearest_curve_tree->inners.back()->curve.ChangeStart(first_curve_point); 329 | if(CArea::m_please_abort)return; 330 | to_do_list_for_MakeOffsets.push_back(nearest_curve_tree->inners.back()); // do it later, in a while loop 331 | if(CArea::m_please_abort)return; 332 | } 333 | } 334 | 335 | void CurveTree::MakeOffsets() 336 | { 337 | to_do_list_for_MakeOffsets.push_back(this); 338 | islands_added.clear(); 339 | 340 | while(to_do_list_for_MakeOffsets.size() > 0) 341 | { 342 | CurveTree* curve_tree = to_do_list_for_MakeOffsets.front(); 343 | to_do_list_for_MakeOffsets.pop_front(); 344 | curve_tree->MakeOffsets2(); 345 | } 346 | } 347 | 348 | void recur(std::list &arealist, const CArea& a1, const CAreaPocketParams ¶ms, int level) 349 | { 350 | //if(level > 3)return; 351 | 352 | // this makes arealist by recursively offsetting a1 inwards 353 | 354 | if(a1.m_curves.size() == 0) 355 | return; 356 | 357 | if(params.from_center) 358 | arealist.push_front(a1); 359 | else 360 | arealist.push_back(a1); 361 | 362 | CArea a_offset = a1; 363 | a_offset.Offset(params.stepover); 364 | 365 | // split curves into new areas 366 | if(CArea::HolesLinked()) 367 | { 368 | for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) 369 | { 370 | CArea a2; 371 | a2.m_curves.push_back(*It); 372 | recur(arealist, a2, params, level + 1); 373 | } 374 | } 375 | else 376 | { 377 | // split curves into new areas 378 | a_offset.Reorder(); 379 | CArea* a2 = NULL; 380 | 381 | for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) 382 | { 383 | CCurve& curve = *It; 384 | if(curve.IsClockwise()) 385 | { 386 | if(a2 != NULL) 387 | a2->m_curves.push_back(curve); 388 | } 389 | else 390 | { 391 | if(a2 != NULL) 392 | recur(arealist, *a2, params, level + 1); 393 | else 394 | a2 = new CArea(); 395 | a2->m_curves.push_back(curve); 396 | } 397 | } 398 | 399 | if(a2 != NULL) 400 | recur(arealist, *a2, params, level + 1); 401 | } 402 | } 403 | 404 | static CArea make_obround(const Point& p0, const Point& p1, double radius) 405 | { 406 | Point dir = p1 - p0; 407 | double d = dir.length(); 408 | dir.normalize(); 409 | Point right(dir.y, -dir.x); 410 | CArea obround; 411 | CCurve c; 412 | if(fabs(radius) < 0.0000001)radius = (radius > 0.0) ? 0.002 : (-0.002); 413 | Point vt0 = p0 + right * radius; 414 | Point vt1 = p1 + right * radius; 415 | Point vt2 = p1 - right * radius; 416 | Point vt3 = p0 - right * radius; 417 | c.append(vt0); 418 | c.append(vt1); 419 | c.append(CVertex(1, vt2, p1)); 420 | c.append(vt3); 421 | c.append(CVertex(1, vt0, p0)); 422 | obround.append(c); 423 | return obround; 424 | } 425 | 426 | static bool feed_possible(const CArea &area_for_feed_possible, const Point& p0, const Point& p1, double tool_radius) 427 | { 428 | CArea obround = make_obround(p0, p1, tool_radius); 429 | obround.Subtract(area_for_feed_possible); 430 | return obround.m_curves.size() == 0; 431 | } 432 | 433 | void MarkOverlappingOffsetIslands(std::list &offset_islands) 434 | { 435 | for(std::list::iterator It1 = offset_islands.begin(); It1 != offset_islands.end(); It1++) 436 | { 437 | std::list::iterator It2 = It1; 438 | It2++; 439 | for(;It2 != offset_islands.end(); It2++) 440 | { 441 | IslandAndOffset &o1 = *It1; 442 | IslandAndOffset &o2 = *It2; 443 | 444 | if(GetOverlapType(o1.offset, o2.offset) == eCrossing) 445 | { 446 | o1.touching_offsets.push_back(&o2); 447 | o2.touching_offsets.push_back(&o1); 448 | } 449 | } 450 | } 451 | } 452 | 453 | void CArea::MakeOnePocketCurve(std::list &curve_list, const CAreaPocketParams ¶ms)const 454 | { 455 | if(CArea::m_please_abort)return; 456 | #if 0 // simple offsets with feed or rapid joins 457 | CArea area_for_feed_possible = *this; 458 | 459 | area_for_feed_possible.Offset(-params.tool_radius - 0.01); 460 | CArea a_offset = *this; 461 | 462 | std::list arealist; 463 | recur(arealist, a_offset, params, 0); 464 | 465 | bool first = true; 466 | 467 | for(std::list::iterator It = arealist.begin(); It != arealist.end(); It++) 468 | { 469 | CArea& area = *It; 470 | for(std::list::iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) 471 | { 472 | CCurve& curve = *It; 473 | if(!first) 474 | { 475 | // try to join these curves with a feed move, if possible and not too long 476 | CCurve &prev_curve = curve_list.back(); 477 | const Point &prev_p = prev_curve.m_vertices.back().m_p; 478 | const Point &next_p = curve.m_vertices.front().m_p; 479 | 480 | if(feed_possible(area_for_feed_possible, prev_p, next_p, params.tool_radius)) 481 | { 482 | // join curves 483 | prev_curve += curve; 484 | } 485 | else 486 | { 487 | curve_list.push_back(curve); 488 | } 489 | } 490 | else 491 | { 492 | curve_list.push_back(curve); 493 | } 494 | first = false; 495 | } 496 | } 497 | #else 498 | pocket_params = ¶ms; 499 | if(m_curves.size() == 0) 500 | { 501 | CArea::m_processing_done += CArea::m_single_area_processing_length; 502 | return; 503 | } 504 | CurveTree top_level(m_curves.front()); 505 | 506 | std::list offset_islands; 507 | 508 | for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) 509 | { 510 | const CCurve& c = *It; 511 | if(It != m_curves.begin()) 512 | { 513 | IslandAndOffset island_and_offset(&c); 514 | offset_islands.push_back(island_and_offset); 515 | top_level.offset_islands.push_back(&(offset_islands.back())); 516 | if(m_please_abort)return; 517 | } 518 | } 519 | 520 | MarkOverlappingOffsetIslands(offset_islands); 521 | 522 | CArea::m_processing_done += CArea::m_single_area_processing_length * 0.1; 523 | 524 | double MakeOffsets_processing_length = CArea::m_single_area_processing_length * 0.8; 525 | CArea::m_after_MakeOffsets_length = CArea::m_processing_done + MakeOffsets_processing_length; 526 | double guess_num_offsets = sqrt(GetArea(true)) * 0.5 / params.stepover; 527 | CArea::m_MakeOffsets_increment = MakeOffsets_processing_length / guess_num_offsets; 528 | 529 | top_level.MakeOffsets(); 530 | if(CArea::m_please_abort)return; 531 | CArea::m_processing_done = CArea::m_after_MakeOffsets_length; 532 | 533 | curve_list.push_back(CCurve()); 534 | CCurve& output = curve_list.back(); 535 | 536 | GetCurveItem::to_do_list.push_back(GetCurveItem(&top_level, output.m_vertices.end())); 537 | 538 | while(GetCurveItem::to_do_list.size() > 0) 539 | { 540 | GetCurveItem item = GetCurveItem::to_do_list.front(); 541 | item.GetCurve(output); 542 | GetCurveItem::to_do_list.pop_front(); 543 | } 544 | 545 | // delete curve_trees non-recursively 546 | std::list CurveTreeDestructList; 547 | for(std::list::iterator It = top_level.inners.begin(); It != top_level.inners.end(); It++) 548 | { 549 | CurveTreeDestructList.push_back(*It); 550 | } 551 | while(CurveTreeDestructList.size() > 0) 552 | { 553 | CurveTree* curve_tree = CurveTreeDestructList.front(); 554 | CurveTreeDestructList.pop_front(); 555 | for(std::list::iterator It = curve_tree->inners.begin(); It != curve_tree->inners.end(); It++) 556 | { 557 | CurveTreeDestructList.push_back(*It); 558 | } 559 | delete curve_tree; 560 | } 561 | 562 | CArea::m_processing_done += CArea::m_single_area_processing_length * 0.1; 563 | #endif 564 | } 565 | 566 | -------------------------------------------------------------------------------- /Box2D.h: -------------------------------------------------------------------------------- 1 | // Box2D.h 2 | // Copyright (c) 2009, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | 7 | #include // for memcpy() prototype 8 | #include // for sqrt() prototype 9 | 10 | class CBox2D{ 11 | public: 12 | Point m_minxy; 13 | Point m_maxxy; 14 | bool m_valid; 15 | 16 | CBox2D():m_valid(false){} 17 | CBox2D(const Point& minxy, const Point& maxxy):m_minxy(minxy), m_maxxy(maxxy), m_valid(true){} 18 | 19 | bool operator==( const CBox2D & rhs ) const 20 | { 21 | if(m_minxy != rhs.m_minxy)return false; 22 | if(m_maxxy != rhs.m_maxxy)return false; 23 | if (m_valid != rhs.m_valid) return(false); 24 | 25 | return(true); 26 | } 27 | 28 | bool operator!=( const CBox2D & rhs ) const { return(! (*this == rhs)); } 29 | 30 | 31 | void Insert(const Point &p){ // insert a point 32 | if(m_valid){ 33 | if(p.x < m_minxy.x)m_minxy.x = p.x; 34 | if(p.y < m_minxy.y)m_minxy.y = p.y; 35 | if(p.x > m_maxxy.x)m_maxxy.x = p.x; 36 | if(p.y > m_maxxy.y)m_maxxy.y = p.y; 37 | } 38 | else 39 | { 40 | m_valid = true; 41 | m_minxy = p; 42 | m_maxxy = p; 43 | } 44 | } 45 | 46 | void Insert(const CBox2D& b){ 47 | if(b.m_valid){ 48 | if(m_valid){ 49 | if(b.m_minxy.x < m_minxy.x)m_minxy.x = b.m_minxy.x; 50 | if(b.m_minxy.y < m_minxy.y)m_minxy.y = b.m_minxy.y; 51 | if(b.m_maxxy.x > m_maxxy.x)m_maxxy.x = b.m_maxxy.x; 52 | if(b.m_maxxy.y > m_maxxy.y)m_maxxy.y = b.m_maxxy.y; 53 | } 54 | else{ 55 | m_valid = b.m_valid; 56 | m_minxy = b.m_minxy; 57 | m_maxxy = b.m_maxxy; 58 | } 59 | } 60 | } 61 | Point Centre() const {return (m_minxy + m_maxxy) * 0.5;} 62 | double Width() const {if(m_valid)return m_maxxy.x - m_minxy.x; else return 0.0;} 63 | double Height() const {if(m_valid)return m_maxxy.y - m_minxy.y; else return 0.0;} 64 | double Radius() const {return sqrt(Width() * Width() + Height() * Height()) /2;} 65 | double MinX() const { return(m_minxy.x); } 66 | double MaxX() const { return(m_maxxy.x); } 67 | double MinY() const { return(m_minxy.y); } 68 | double MaxY() const { return(m_maxxy.y); } 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(area) 2 | 3 | cmake_minimum_required(VERSION 2.4) 4 | 5 | # Turn compiler warnings up to 11, at least with gcc. 6 | if (CMAKE_BUILD_TOOL MATCHES "make") 7 | MESSAGE(STATUS "setting gcc options: -Wall -Werror -Wno-deprecated -pedantic-errors") 8 | # NON-optimized build: 9 | # add_definitions( -Wall -Wno-deprecated -Werror -pedantic-errors) 10 | add_definitions(-fPIC) 11 | endif (CMAKE_BUILD_TOOL MATCHES "make") 12 | 13 | option(BUILD_TYPE 14 | "Build type: Release=ON/Debug=OFF " ON) 15 | option(USE_CLIPPER_FOR_CPP 16 | "Use clipper library (ON) or kbool (OFF) for C++ portion of libarea " ON) 17 | option(USE_CLIPPER_FOR_PYTHON 18 | "Use clipper library (ON) or kbool (OFF) for Python portion of libarea " ON) 19 | 20 | if (BUILD_TYPE) 21 | MESSAGE(STATUS " CMAKE_BUILD_TYPE = Release") 22 | set(CMAKE_BUILD_TYPE Release) 23 | endif(BUILD_TYPE) 24 | 25 | if (NOT BUILD_TYPE) 26 | MESSAGE(STATUS " CMAKE_BUILD_TYPE = Debug") 27 | set(CMAKE_BUILD_TYPE Debug) 28 | endif(NOT BUILD_TYPE) 29 | 30 | # this figures out the Python include directories and adds them to the 31 | # header file search path 32 | execute_process( 33 | COMMAND python-config --includes 34 | COMMAND sed -r "s/-I//g; s/ +/;/g" 35 | COMMAND tr -d '\n' 36 | OUTPUT_VARIABLE Python_Includes 37 | ) 38 | message(STATUS "Python include dir:" ${Python_Includes}) 39 | 40 | include_directories(${Python_Includes}) 41 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 42 | 43 | find_package( Boost COMPONENTS python REQUIRED) # find BOOST and boost-python 44 | if(Boost_FOUND) 45 | include_directories(${Boost_INCLUDE_DIRS}) 46 | MESSAGE(STATUS "found Boost: " ${Boost_LIB_VERSION}) 47 | MESSAGE(STATUS "boost-incude dirs are: " ${Boost_INCLUDE_DIRS}) 48 | MESSAGE(STATUS "boost-python lib is: " ${Boost_PYTHON_LIBRARY}) 49 | MESSAGE(STATUS "boost_LIBRARY_DIRS is: " ${Boost_LIBRARY_DIRS}) 50 | MESSAGE(STATUS "Boost_LIBRARIES is: " ${Boost_LIBRARIES}) 51 | endif() 52 | 53 | # this defines the source-files for library 54 | set(AREA_SRC_COMMON 55 | Arc.cpp 56 | Area.cpp 57 | AreaDxf.cpp 58 | AreaOrderer.cpp 59 | AreaPocket.cpp 60 | Circle.cpp 61 | Curve.cpp 62 | dxf.cpp 63 | 64 | kurve/Construction.cpp 65 | kurve/Finite.cpp 66 | kurve/kurve.cpp 67 | kurve/Matrix.cpp 68 | kurve/offset.cpp 69 | ) 70 | 71 | 72 | set(AREA_SRC_CLIPPER 73 | AreaClipper.cpp 74 | 75 | clipper.cpp 76 | ) 77 | 78 | # this defines the additional source-files for python module (wrapper to libarea) 79 | set(PYAREA_SRC 80 | PythonStuff.cpp 81 | ) 82 | 83 | # this defines the headers 84 | if(DEFINED INCLUDE_INSTALL_DIR) 85 | set(includedir ${INCLUDE_INSTALL_DIR}) 86 | else(DEFINED INCLUDE_INSTALL_DIR) 87 | set(INCLUDE_INSTALL_DIR include) 88 | set(includedir ${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR}) 89 | endif(DEFINED INCLUDE_INSTALL_DIR) 90 | 91 | file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/kurve/*.h") 92 | install(FILES ${headers} DESTINATION ${INCLUDE_INSTALL_DIR}/area/kurve COMPONENT headers) 93 | file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h") 94 | install(FILES ${headers} DESTINATION ${INCLUDE_INSTALL_DIR}/area COMPONENT headers) 95 | 96 | # include directories 97 | 98 | # this makes the shared library 99 | if (USE_CLIPPER_FOR_CPP) 100 | add_library( 101 | libarea 102 | SHARED 103 | ${AREA_SRC_COMMON} 104 | ${AREA_SRC_CLIPPER} 105 | ) 106 | else (USE_CLIPPER_FOR_CPP) 107 | add_library( 108 | libarea 109 | SHARED 110 | ${AREA_SRC_COMMON} 111 | 112 | ) 113 | endif (USE_CLIPPER_FOR_CPP) 114 | set_target_properties(libarea PROPERTIES PREFIX "") 115 | set_target_properties(libarea PROPERTIES SOVERSION 0) 116 | 117 | # this part allow to support multi-arch 118 | # ie. Debian builder sets correctly the target path according to architecture 119 | # e.g. /usr/lib/i386-linux-gnu, /usr/lib/x86_64-linux-gnu 120 | # TODO: Support this feature 121 | #if(DEFINED CMAKE_INSTALL_LIBDIR) 122 | # set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) 123 | #else(DEFINED CMAKE_INSTALL_LIBDIR) 124 | # set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib) 125 | #endif(DEFINED CMAKE_INSTALL_LIBDIR) 126 | 127 | install(TARGETS libarea LIBRARY DESTINATION lib/ COMPONENT libraries) 128 | message(STATUS "Library will be installed to: " ${CMAKE_INSTALL_PREFIX}/lib) 129 | 130 | 131 | # this makes the Python module 132 | if (USE_CLIPPER_FOR_PYTHON) 133 | add_library( 134 | area 135 | MODULE 136 | ${AREA_SRC_COMMON} 137 | ${AREA_SRC_CLIPPER} 138 | ${PYAREA_SRC} 139 | ) 140 | else (USE_CLIPPER_FOR_PYTHON) 141 | add_library( 142 | area 143 | MODULE 144 | ${AREA_SRC_COMMON} 145 | ${AREA_SRC_KBOOL} 146 | ${PYAREA_SRC} 147 | ) 148 | endif (USE_CLIPPER_FOR_PYTHON) 149 | target_link_libraries(area ${Boost_LIBRARIES}) 150 | set_target_properties(area PROPERTIES PREFIX "") 151 | 152 | # this figures out where to install the Python modules 153 | execute_process( 154 | COMMAND python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" 155 | OUTPUT_VARIABLE Python_site_packages 156 | OUTPUT_STRIP_TRAILING_WHITESPACE 157 | ) 158 | 159 | # strip away /usr/local/ because that is what CMAKE_INSTALL_PREFIX is set to 160 | # also, since there is no leading "/", it makes ${Python_site_packages} a relative path. 161 | STRING(REGEX REPLACE "/usr/local/(.*)$" "\\1" Python_site_packages "${Python_site_packages}" ) 162 | STRING(REGEX REPLACE "/usr/(.*)$" "\\1" Python_site_packages "${Python_site_packages}" ) 163 | 164 | message(STATUS "Python module will be installed to: " ${CMAKE_INSTALL_PREFIX}/${Python_site_packages}) 165 | 166 | # this installs the python library 167 | install( 168 | TARGETS area 169 | LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${Python_site_packages} 170 | ) 171 | -------------------------------------------------------------------------------- /Circle.cpp: -------------------------------------------------------------------------------- 1 | // Circle.cpp 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #include "Circle.h" 6 | 7 | Circle::Circle(const Point& p0, const Point& p1, const Point& p2) 8 | { 9 | // from TangentCircles in http://code.google.com/p/heekscad/source/browse/trunk/src/Geom.cpp 10 | 11 | // set default values, in case this fails 12 | m_radius = 0.0; 13 | m_c = Point(0, 0); 14 | 15 | double x1 = p0.x; 16 | double y1 = p0.y; 17 | double x2 = p1.x; 18 | double y2 = p1.y; 19 | double x3 = p2.x; 20 | double y3 = p2.y; 21 | 22 | double a = 2 * (x1 - x2); 23 | double b = 2 * (y1 - y2); 24 | double d = (x1 * x1 + y1 * y1) - (x2 * x2 + y2 * y2); 25 | 26 | double A = 2 * (x1 - x3); 27 | double B = 2 * (y1 - y3); 28 | double D = (x1 * x1 + y1 * y1) - (x3 * x3 + y3 * y3); 29 | 30 | double aBmbA = (a*B - b*A); // aB - bA 31 | 32 | // x = k + Kr where 33 | double k = (B*d - b*D) / aBmbA; 34 | 35 | // y = l + Lr where 36 | double l = (-A*d + a*D)/ aBmbA; 37 | 38 | double qa = -1; 39 | double qb = 0.0; 40 | double qc = k*k + x1*x1 -2*k*x1 + l*l + y1*y1 - 2*l*y1; 41 | 42 | // solve the quadratic equation, r = (-b +- sqrt(b*b - 4*a*c))/(2 * a) 43 | for(int qs = 0; qs<2; qs++){ 44 | double bb = qb*qb; 45 | double ac4 = 4*qa*qc; 46 | if(ac4 <= bb){ 47 | double r = (-qb + ((qs == 0) ? 1 : -1) * sqrt(bb - ac4))/(2 * qa); 48 | double x = k; 49 | double y = l; 50 | 51 | // set the circle 52 | if(r >= 0.0){ 53 | m_c = Point(x, y); 54 | m_radius = r; 55 | } 56 | } 57 | } 58 | } 59 | 60 | bool Circle::PointIsOn(const Point& p, double accuracy) 61 | { 62 | double rp = p.dist(m_c); 63 | bool on = fabs(m_radius - rp) < accuracy; 64 | return on; 65 | } 66 | 67 | bool Circle::LineIsOn(const Point& p0, const Point& p1, double accuracy) 68 | { 69 | // checks the points are on the arc, to the given accuracy, and the mid point of the line. 70 | 71 | if(!PointIsOn(p0, accuracy))return false; 72 | if(!PointIsOn(p1, accuracy))return false; 73 | 74 | Point mid = Point((p0 + p1)/2); 75 | if(!PointIsOn(mid, accuracy))return false; 76 | 77 | return true; 78 | } -------------------------------------------------------------------------------- /Circle.h: -------------------------------------------------------------------------------- 1 | // Circle.h 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | 7 | #include "Point.h" 8 | 9 | class Circle{ 10 | public: 11 | Point m_c; 12 | double m_radius; 13 | 14 | Circle(const Point& c, double radius):m_c(c), m_radius(radius){} 15 | Circle(const Point& p0, const Point& p1, const Point& p2); // circle through three points 16 | 17 | bool PointIsOn(const Point& p, double accuracy); 18 | bool LineIsOn(const Point& p0, const Point& p1, double accuracy); 19 | }; -------------------------------------------------------------------------------- /Curve.h: -------------------------------------------------------------------------------- 1 | // Curve.h 2 | // Copyright 2011, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include "Point.h" 11 | #include "Box2D.h" 12 | 13 | class Line{ 14 | public: 15 | Point p0; 16 | Point v; 17 | 18 | // constructors 19 | Line(const Point& P0, const Point& V); 20 | 21 | double Dist(const Point& p)const; 22 | }; 23 | 24 | class CArc; 25 | 26 | class CVertex 27 | { 28 | public: 29 | int m_type; // 0 - line ( or start point ), 1 - anti-clockwise arc, -1 - clockwise arc 30 | Point m_p; // end point 31 | Point m_c; // centre point in absolute coordinates 32 | int m_user_data; 33 | 34 | CVertex():m_type(0), m_p(Point(0, 0)), m_c(Point(0,0)), m_user_data(0){} 35 | CVertex(int type, const Point& p, const Point& c, int user_data = 0); 36 | CVertex(const Point& p, int user_data = 0); 37 | }; 38 | 39 | class Span 40 | { 41 | Point NearestPointNotOnSpan(const Point& p)const; 42 | double Parameter(const Point& p)const; 43 | Point NearestPointToSpan(const Span& p, double &d)const; 44 | 45 | static const Point null_point; 46 | static const CVertex null_vertex; 47 | 48 | public: 49 | bool m_start_span; 50 | Point m_p; 51 | CVertex m_v; 52 | Span(); 53 | Span(const Point& p, const CVertex& v, bool start_span = false):m_start_span(start_span), m_p(p), m_v(v){} 54 | Point NearestPoint(const Point& p)const; 55 | Point NearestPoint(const Span& p, double *d = NULL)const; 56 | void GetBox(CBox2D &box); 57 | double IncludedAngle()const; 58 | double GetArea()const; 59 | bool On(const Point& p, double* t = NULL)const; 60 | Point MidPerim(double d)const; 61 | Point MidParam(double param)const; 62 | double Length()const; 63 | Point GetVector(double fraction)const; 64 | void Intersect(const Span& s, std::list &pts)const; // finds all the intersection points between two spans 65 | }; 66 | 67 | class CCurve 68 | { 69 | // a closed curve, please make sure you add an end point, the same as the start point 70 | 71 | protected: 72 | void AddArcOrLines(bool check_for_arc, std::list &new_vertices, std::list& might_be_an_arc, CArc &arc, bool &arc_found, bool &arc_added); 73 | bool CheckForArc(const CVertex& prev_vt, std::list& might_be_an_arc, CArc &arc); 74 | 75 | public: 76 | std::list m_vertices; 77 | void append(const CVertex& vertex); 78 | 79 | void FitArcs(); 80 | void UnFitArcs(); 81 | Point NearestPoint(const Point& p)const; 82 | Point NearestPoint(const CCurve& p, double *d = NULL)const; 83 | Point NearestPoint(const Span& p, double *d = NULL)const; 84 | void GetBox(CBox2D &box); 85 | void Reverse(); 86 | double GetArea()const; 87 | bool IsClockwise()const{return GetArea()>0;} 88 | bool IsClosed()const; 89 | void ChangeStart(const Point &p); 90 | void ChangeEnd(const Point &p); 91 | bool Offset(double leftwards_value); 92 | void OffsetForward(double forwards_value, bool refit_arcs = true); // for drag-knife compensation 93 | void Break(const Point &p); 94 | void ExtractSeparateCurves(const std::list &ordered_points, std::list &separate_curves)const; 95 | double Perim()const; 96 | Point PerimToPoint(double perim)const; 97 | double PointToPerim(const Point& p)const; 98 | void GetSpans(std::list &spans)const; 99 | void RemoveTinySpans(); 100 | void operator+=(const CCurve& p); 101 | void SpanIntersections(const Span& s, std::list &pts)const; 102 | void CurveIntersections(const CCurve& c, std::list &pts)const; 103 | }; 104 | 105 | void tangential_arc(const Point &p0, const Point &p1, const Point &v0, Point &c, int &dir); 106 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) <2015>, 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Point.h: -------------------------------------------------------------------------------- 1 | // Point.h 2 | 3 | // Copyright 2011, Dan Heeks 4 | // This program is released under the BSD license. See the file COPYING for details. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "kurve/geometry.h" 10 | 11 | class Point{ 12 | public: 13 | // can be a position, or a vector 14 | double x, y; 15 | 16 | Point():x(0.0), y(0.0){} 17 | Point(double X, double Y):x(X), y(Y){} 18 | Point(const double* p):x(p[0]), y(p[1]){} 19 | Point(const Point& p0, const Point& p1):x(p1.x - p0.x), y(p1.y - p0.y){} // vector from p0 to p1 20 | 21 | static double tolerance; 22 | 23 | const Point operator+(const Point& p)const{return Point(x + p.x, y + p.y);} 24 | const Point operator-(const Point& p)const{return Point(x - p.x, y - p.y);} 25 | const Point operator*(double d)const{return Point(x * d, y * d);} 26 | const Point operator/(double d)const{return Point(x / d, y / d);} 27 | bool operator==(const Point& p)const{return fabs(x-p.x) 15 | #define _DEBUG 16 | #else 17 | #include 18 | #endif 19 | 20 | #ifdef __GNUG__ 21 | #pragma implementation 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "clipper.hpp" 34 | using namespace ClipperLib; 35 | 36 | 37 | namespace bp = boost::python; 38 | 39 | boost::python::list getVertices(const CCurve& curve) { 40 | boost::python::list vlist; 41 | BOOST_FOREACH(const CVertex& vertex, curve.m_vertices) { 42 | vlist.append(vertex); 43 | } 44 | return vlist; 45 | } 46 | 47 | boost::python::list getCurves(const CArea& area) { 48 | boost::python::list clist; 49 | BOOST_FOREACH(const CCurve& curve, area.m_curves) { 50 | clist.append(curve); 51 | } 52 | return clist; 53 | } 54 | 55 | boost::python::tuple transformed_point(const geoff_geometry::Matrix &matrix, double x, double y, double z) 56 | { 57 | geoff_geometry::Point3d p(x,y,z); 58 | p = p.Transform(matrix); 59 | 60 | return bp::make_tuple(p.x,p.y,p.z); 61 | } 62 | 63 | static void print_curve(const CCurve& c) 64 | { 65 | unsigned int nvertices = c.m_vertices.size(); 66 | printf("number of vertices = %d\n", nvertices); 67 | int i = 0; 68 | for(std::list::const_iterator It = c.m_vertices.begin(); It != c.m_vertices.end(); It++, i++) 69 | { 70 | const CVertex& vertex = *It; 71 | printf("vertex %d type = %d, x = %g, y = %g", i+1, vertex.m_type, vertex.m_p.x / CArea::m_units, vertex.m_p.y / CArea::m_units); 72 | if(vertex.m_type)printf(", xc = %g, yc = %g", vertex.m_c.x / CArea::m_units, vertex.m_c.y / CArea::m_units); 73 | printf("\n"); 74 | } 75 | } 76 | 77 | static void print_area(const CArea &a) 78 | { 79 | for(std::list::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) 80 | { 81 | const CCurve& curve = *It; 82 | print_curve(curve); 83 | } 84 | } 85 | 86 | static unsigned int num_vertices(const CCurve& curve) 87 | { 88 | return curve.m_vertices.size(); 89 | } 90 | 91 | static CVertex FirstVertex(const CCurve& curve) 92 | { 93 | return curve.m_vertices.front(); 94 | } 95 | 96 | static CVertex LastVertex(const CCurve& curve) 97 | { 98 | return curve.m_vertices.back(); 99 | } 100 | 101 | static void set_units(double units) 102 | { 103 | CArea::m_units = units; 104 | } 105 | 106 | static double get_units() 107 | { 108 | return CArea::m_units; 109 | } 110 | 111 | static bool holes_linked() 112 | { 113 | return CArea::HolesLinked(); 114 | } 115 | 116 | static CArea AreaFromDxf(const char* filepath) 117 | { 118 | CArea area; 119 | AreaDxfRead dxf(&area, filepath); 120 | dxf.DoRead(); 121 | return area; 122 | } 123 | 124 | static void append_point(CCurve& c, const Point& p) 125 | { 126 | c.m_vertices.push_back(CVertex(p)); 127 | } 128 | 129 | static boost::python::tuple nearest_point_to_curve(CCurve& c1, const CCurve& c2) 130 | { 131 | double dist; 132 | Point p = c1.NearestPoint(c2, &dist); 133 | 134 | return bp::make_tuple(p, dist); 135 | } 136 | 137 | boost::python::list MakePocketToolpath(const CArea& a, double tool_radius, double extra_offset, double stepover, bool from_center, bool use_zig_zag, double zig_angle) 138 | { 139 | std::list toolpath; 140 | 141 | CAreaPocketParams params(tool_radius, extra_offset, stepover, from_center, use_zig_zag ? ZigZagPocketMode : SpiralPocketMode, zig_angle); 142 | a.SplitAndMakePocketToolpath(toolpath, params); 143 | 144 | boost::python::list clist; 145 | BOOST_FOREACH(const CCurve& c, toolpath) { 146 | clist.append(c); 147 | } 148 | return clist; 149 | } 150 | 151 | boost::python::list SplitArea(const CArea& a) 152 | { 153 | std::list areas; 154 | a.Split(areas); 155 | 156 | boost::python::list alist; 157 | BOOST_FOREACH(const CArea& a, areas) { 158 | alist.append(a); 159 | } 160 | return alist; 161 | } 162 | 163 | void dxfArea(CArea& area, const char* str) 164 | { 165 | area = CArea(); 166 | } 167 | 168 | boost::python::list getCurveSpans(const CCurve& c) 169 | { 170 | boost::python::list span_list; 171 | const Point *prev_p = NULL; 172 | 173 | for(std::list::const_iterator VIt = c.m_vertices.begin(); VIt != c.m_vertices.end(); VIt++) 174 | { 175 | const CVertex& vertex = *VIt; 176 | 177 | if(prev_p) 178 | { 179 | span_list.append(Span(*prev_p, vertex)); 180 | } 181 | prev_p = &(vertex.m_p); 182 | } 183 | 184 | return span_list; 185 | } 186 | 187 | Span getFirstCurveSpan(const CCurve& c) 188 | { 189 | if(c.m_vertices.size() < 2)return Span(); 190 | 191 | std::list::const_iterator VIt = c.m_vertices.begin(); 192 | const Point &p = (*VIt).m_p; 193 | VIt++; 194 | return Span(p, *VIt, true); 195 | } 196 | 197 | Span getLastCurveSpan(const CCurve& c) 198 | { 199 | if(c.m_vertices.size() < 2)return Span(); 200 | 201 | std::list::const_reverse_iterator VIt = c.m_vertices.rbegin(); 202 | const CVertex &v = (*VIt); 203 | VIt++; 204 | 205 | return Span((*VIt).m_p, v, c.m_vertices.size() == 2); 206 | } 207 | 208 | bp::tuple TangentialArc(const Point &p0, const Point &p1, const Point &v0) 209 | { 210 | Point c; 211 | int dir; 212 | tangential_arc(p0, p1, v0, c, dir); 213 | 214 | return bp::make_tuple(c, dir); 215 | } 216 | 217 | boost::python::list spanIntersect(const Span& span1, const Span& span2) { 218 | boost::python::list plist; 219 | std::list pts; 220 | span1.Intersect(span2, pts); 221 | BOOST_FOREACH(const Point& p, pts) { 222 | plist.append(p); 223 | } 224 | return plist; 225 | } 226 | 227 | //Matrix(boost::python::list &l){} 228 | 229 | boost::shared_ptr matrix_constructor(const boost::python::list& lst) { 230 | double m[16] = {1,0,0,0,0,1,0,0, 0,0,1,0, 0,0,0,1}; 231 | 232 | boost::python::ssize_t n = boost::python::len(lst); 233 | int j = 0; 234 | for(boost::python::ssize_t i=0;i(elem.attr("__float__")()); 237 | j++; 238 | if(j>=16)break; 239 | } 240 | 241 | return boost::shared_ptr( new geoff_geometry::Matrix(m) ); 242 | } 243 | 244 | boost::python::list InsideCurves(const CArea& a, const CCurve& curve) { 245 | boost::python::list plist; 246 | 247 | std::list curves_inside; 248 | a.InsideCurves(curve, curves_inside); 249 | BOOST_FOREACH(const CCurve& c, curves_inside) { 250 | plist.append(c); 251 | } 252 | return plist; 253 | } 254 | 255 | boost::python::list CurveIntersections(const CCurve& c1, const CCurve& c2) { 256 | boost::python::list plist; 257 | 258 | std::list pts; 259 | c1.CurveIntersections(c2, pts); 260 | BOOST_FOREACH(const Point& p, pts) { 261 | plist.append(p); 262 | } 263 | return plist; 264 | } 265 | 266 | boost::python::list AreaIntersections(const CArea& a, const CCurve& c2) { 267 | boost::python::list plist; 268 | 269 | std::list pts; 270 | a.CurveIntersections(c2, pts); 271 | BOOST_FOREACH(const Point& p, pts) { 272 | plist.append(p); 273 | } 274 | return plist; 275 | } 276 | 277 | double AreaGetArea(const CArea& a) 278 | { 279 | return a.GetArea(); 280 | } 281 | 282 | BOOST_PYTHON_MODULE(area) { 283 | bp::class_("Point") 284 | .def(bp::init()) 285 | .def(bp::init()) 286 | .def(bp::other() * bp::self) 287 | .def(bp::self * bp::other()) 288 | .def(bp::self / bp::other()) 289 | .def(bp::self * bp::other()) 290 | .def(bp::self - bp::other()) 291 | .def(bp::self + bp::other()) 292 | .def(bp::self ^ bp::other()) 293 | .def(bp::self == bp::other()) 294 | .def(bp::self != bp::other()) 295 | .def(-bp::self) 296 | .def(~bp::self) 297 | .def("dist", &Point::dist) 298 | .def("length", &Point::length) 299 | .def("normalize", &Point::normalize) 300 | .def("Rotate", static_cast< void (Point::*)(double, double) >(&Point::Rotate)) 301 | .def("Rotate", static_cast< void (Point::*)(double) >(&Point::Rotate)) 302 | .def_readwrite("x", &Point::x) 303 | .def_readwrite("y", &Point::y) 304 | .def("Transform", &Point::Transform) 305 | ; 306 | 307 | bp::class_("Vertex") 308 | .def(bp::init()) 309 | .def(bp::init()) 310 | .def(bp::init()) 311 | .def(bp::init()) 312 | .def_readwrite("type", &CVertex::m_type) 313 | .def_readwrite("p", &CVertex::m_p) 314 | .def_readwrite("c", &CVertex::m_c) 315 | .def_readwrite("user_data", &CVertex::m_user_data) 316 | ; 317 | 318 | bp::class_("Span") 319 | .def(bp::init()) 320 | .def(bp::init()) 321 | .def("NearestPoint", static_cast< Point (Span::*)(const Point& p)const >(&Span::NearestPoint)) 322 | .def("NearestPoint", static_cast< Point (Span::*)(const Span& p, double *d)const >(&Span::NearestPoint)) 323 | .def("GetBox", &Span::GetBox) 324 | .def("IncludedAngle", &Span::IncludedAngle) 325 | .def("GetArea", &Span::GetArea) 326 | .def("On", &Span::On) 327 | .def("MidPerim", &Span::MidPerim) 328 | .def("MidParam", &Span::MidParam) 329 | .def("Length", &Span::Length) 330 | .def("GetVector", &Span::GetVector) 331 | .def("Intersect", &spanIntersect) 332 | .def_readwrite("p", &Span::m_p) 333 | .def_readwrite("v", &Span::m_v) 334 | ; 335 | 336 | bp::class_("Curve") 337 | .def(bp::init()) 338 | .def("getVertices", &getVertices) 339 | .def("append",&CCurve::append) 340 | .def("append",&append_point) 341 | .def("text", &print_curve) 342 | .def("NearestPoint", static_cast< Point (CCurve::*)(const Point& p)const >(&CCurve::NearestPoint)) 343 | .def("NearestPoint", &nearest_point_to_curve) 344 | .def("Reverse", &CCurve::Reverse) 345 | .def("getNumVertices", &num_vertices) 346 | .def("FirstVertex", &FirstVertex) 347 | .def("LastVertex", &LastVertex) 348 | .def("GetArea", &CCurve::GetArea) 349 | .def("IsClockwise", &CCurve::IsClockwise) 350 | .def("IsClosed", &CCurve::IsClosed) 351 | .def("ChangeStart",&CCurve::ChangeStart) 352 | .def("ChangeEnd",&CCurve::ChangeEnd) 353 | .def("Offset",&CCurve::Offset) 354 | .def("OffsetForward",&CCurve::OffsetForward) 355 | .def("GetSpans",&getCurveSpans) 356 | .def("GetFirstSpan",&getFirstCurveSpan) 357 | .def("GetLastSpan",&getLastCurveSpan) 358 | .def("Break",&CCurve::Break) 359 | .def("Perim",&CCurve::Perim) 360 | .def("PerimToPoint",&CCurve::PerimToPoint) 361 | .def("PointToPerim",&CCurve::PointToPerim) 362 | .def("FitArcs",&CCurve::FitArcs) 363 | .def("UnFitArcs",&CCurve::UnFitArcs) 364 | .def("Intersections",&CurveIntersections) 365 | ; 366 | 367 | bp::class_("Box") 368 | .def(bp::init()) 369 | .def("MinX", &CBox2D::MinX) 370 | .def("MaxX", &CBox2D::MaxX) 371 | .def("MinY", &CBox2D::MinY) 372 | .def("MaxY", &CBox2D::MaxY) 373 | ; 374 | 375 | bp::class_("Area") 376 | .def(bp::init()) 377 | .def("getCurves", &getCurves) 378 | .def("append",&CArea::append) 379 | .def("Subtract",&CArea::Subtract) 380 | .def("Intersect",&CArea::Intersect) 381 | .def("Union",&CArea::Union) 382 | .def("Offset",&CArea::Offset) 383 | .def("FitArcs",&CArea::FitArcs) 384 | .def("text", &print_area) 385 | .def("num_curves", &CArea::num_curves) 386 | .def("NearestPoint", &CArea::NearestPoint) 387 | .def("GetBox", &CArea::GetBox) 388 | .def("Reorder", &CArea::Reorder) 389 | .def("MakePocketToolpath", &MakePocketToolpath) 390 | .def("Split", &SplitArea) 391 | .def("InsideCurves", &InsideCurves) 392 | .def("Thicken", &CArea::Thicken) 393 | .def("Intersections",&AreaIntersections) 394 | .def("GetArea",&AreaGetArea) 395 | ; 396 | 397 | bp::class_ > ("Matrix") 398 | .def(bp::init()) 399 | .def("__init__", bp::make_constructor(&matrix_constructor)) 400 | .def("TransformedPoint", &transformed_point) 401 | .def("Multiply", &geoff_geometry::Matrix::Multiply) 402 | ; 403 | 404 | bp::def("set_units", set_units); 405 | bp::def("get_units", get_units); 406 | bp::def("holes_linked", holes_linked); 407 | bp::def("AreaFromDxf", AreaFromDxf); 408 | bp::def("TangentialArc", TangentialArc); 409 | } 410 | -------------------------------------------------------------------------------- /PythonStuff.h: -------------------------------------------------------------------------------- 1 | // PythonStuff.h 2 | 3 | // Copyright 2011, Dan Heeks 4 | // This program is released under the BSD license. See the file COPYING for details. 5 | 6 | extern void Message(const char*); 7 | 8 | void PythonInit(); 9 | void PythonFinish(); 10 | 11 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | libarea is a CAM-related software for profile and pocketing operation. 2 | 3 | This project provides library and associated python-modules to compute profile and pocket operations. 4 | 5 | Written by Dan Heeks 6 | 7 | libarea originally used kbool http://boolean.klaasholwerda.nl/bool.html written by Klaas Holwerda for calculating intersections. Since kbool uses a GPL license and HeeksCNC has a New BSD license, Dan rewrote some of the code to use the more liberally licensed (Boost license, which is compatible with LGPLv2) clipper library,written by Angus Johnson, to replace kbool. Clipper can be found here: http://sourceforge.net/p/polyclipping/code/HEAD/tree/trunk/. The clipper library is include with libarea for convenience. 8 | 9 | libarea is used by HeeksCNC and FreeCAD's Path workbench. 10 | 11 | As more evidence of the legitimate New BSD licensing of libarea, I am including an email exchange between Dan Heeks, Geoff Hawkesford, and myself. In this exchange of emails, Geoff cleared up his use of the New BSD license for the code in the 'kurve' directory. See below: 12 | 13 | Subject: question about kurve license 14 | Dan Falck 15 | 1/19/12 16 | 17 | to Dan Heeks: 18 | Hi Dan, 19 | 20 | Do you know if g.j.hawkesford wanted his kurve code released under the 21 | 'New BSD' or original BSD license? I can't find the 'COPYING' file 22 | that is mentioned at the beginning of all the files in /kurve . 23 | 24 | I want to get everything in line with 'New BSD' , Boost, and LGPL 25 | licenses before I go too far in my FreeCAD CAM module. I plan on using 26 | clipper instead of bool for the area code. 27 | 28 | Thanks, 29 | Dan 30 | 31 | Dan Heeks 32 | 1/20/12 33 | 34 | to me 35 | I can't find his email. But I am sure that he has no idea that there 36 | is a difference between BSD and "New BSD". I will ask him when I see 37 | him next Wednesday also. 38 | 39 | Dan Heeks 40 | 1/30/12 41 | 42 | to Geoff, me 43 | Geoff, 44 | 45 | My friend, Dan Falck, from Portland Oregon, is asking about your geometry code that I released in my HeeksCNC project. 46 | It looks like we put "BSD license", but it is more normal to put "New BSD license". 47 | I think the difference is the removal of a clause about having to include an acknowledgement of the original source in all advertising material. 48 | I presume you don't mind if we change it from "BSD" to "New BSD"? 49 | http://en.wikipedia.org/wiki/BSD_licenses 50 | 51 | 52 | Geoff Hawkesford 53 | 1/31/12 54 | 55 | to Dan, me 56 | That’s ok for me. 57 | Geoff 58 | 59 | 60 | From: Dan Heeks 61 | Sent: Monday, January 30, 2012 9:34 PM 62 | To: Geoff Hawkesford 63 | Cc: Dan Falck 64 | Subject: Re: question about kurve license 65 | 66 | 67 | Dan Heeks 68 | 1/31/12 69 | 70 | to Geoff, me 71 | Thanks. 72 | 73 | 74 | Dan Falck 75 | 1/31/12 76 | 77 | to Geoff, Dan 78 | Thank you so much. 79 | 80 | -------------------------------------------------------------------------------- /area clipper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "area clipper", "area clipper.vcproj", "{35834132-FB6C-41C4-BFB2-F7A964E06153}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {35834132-FB6C-41C4-BFB2-F7A964E06153}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {35834132-FB6C-41C4-BFB2-F7A964E06153}.Debug|Win32.Build.0 = Debug|Win32 14 | {35834132-FB6C-41C4-BFB2-F7A964E06153}.Release|Win32.ActiveCfg = Release|Win32 15 | {35834132-FB6C-41C4-BFB2-F7A964E06153}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /area clipper.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 53 | 56 | 59 | 62 | 72 | 75 | 78 | 81 | 84 | 87 | 90 | 93 | 94 | 102 | 105 | 108 | 111 | 114 | 117 | 129 | 132 | 135 | 138 | 150 | 153 | 156 | 159 | 162 | 165 | 168 | 172 | 173 | 174 | 175 | 176 | 177 | 182 | 185 | 186 | 189 | 190 | 193 | 194 | 197 | 198 | 201 | 202 | 205 | 206 | 209 | 210 | 213 | 214 | 217 | 218 | 221 | 222 | 225 | 226 | 229 | 230 | 233 | 234 | 237 | 238 | 241 | 242 | 245 | 246 | 247 | 252 | 255 | 256 | 259 | 260 | 263 | 264 | 267 | 268 | 271 | 272 | 275 | 276 | 279 | 280 | 283 | 284 | 287 | 288 | 291 | 292 | 295 | 296 | 297 | 302 | 303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /area.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "area", "area.vcproj", "{25834132-FB6C-41C4-BFB2-F7A964E06153}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {25834132-FB6C-41C4-BFB2-F7A964E06153}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {25834132-FB6C-41C4-BFB2-F7A964E06153}.Debug|Win32.Build.0 = Debug|Win32 14 | {25834132-FB6C-41C4-BFB2-F7A964E06153}.Release|Win32.ActiveCfg = Release|Win32 15 | {25834132-FB6C-41C4-BFB2-F7A964E06153}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /area.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 53 | 56 | 59 | 62 | 72 | 75 | 78 | 81 | 84 | 87 | 90 | 93 | 94 | 102 | 105 | 108 | 111 | 114 | 117 | 129 | 132 | 135 | 138 | 150 | 153 | 156 | 159 | 162 | 165 | 168 | 172 | 173 | 174 | 175 | 176 | 177 | 182 | 185 | 186 | 189 | 190 | 193 | 194 | 197 | 198 | 201 | 202 | 205 | 206 | 209 | 210 | 213 | 214 | 217 | 218 | 221 | 222 | 225 | 226 | 229 | 230 | 233 | 234 | 237 | 238 | 241 | 242 | 245 | 246 | 249 | 250 | 253 | 254 | 257 | 258 | 261 | 262 | 265 | 266 | 269 | 270 | 273 | 274 | 277 | 278 | 281 | 282 | 283 | 288 | 291 | 292 | 295 | 296 | 299 | 300 | 303 | 304 | 307 | 308 | 311 | 312 | 315 | 316 | 319 | 320 | 323 | 324 | 327 | 328 | 331 | 332 | 335 | 336 | 339 | 340 | 343 | 344 | 347 | 348 | 351 | 352 | 355 | 356 | 359 | 360 | 363 | 364 | 367 | 368 | 371 | 372 | 375 | 376 | 377 | 382 | 383 | 384 | 385 | 386 | 387 | -------------------------------------------------------------------------------- /clipper.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielfalck/libarea/51e67781b6c5e451b2732941ff88fa65797e4b41/clipper.cpp -------------------------------------------------------------------------------- /clipper.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.2.0 * 5 | * Date : 2 October 2014 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2014 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | #ifndef clipper_hpp 35 | #define clipper_hpp 36 | 37 | #define CLIPPER_VERSION "6.2.0" 38 | 39 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 40 | //improve performance but coordinate values are limited to the range +/- 46340 41 | //#define use_int32 42 | 43 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 44 | //#define use_xyz 45 | 46 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 47 | //#define use_lines 48 | 49 | //use_deprecated: Enables temporary support for the obsolete functions 50 | //#define use_deprecated 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | namespace ClipperLib { 62 | 63 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 64 | enum PolyType { ptSubject, ptClip }; 65 | //By far the most widely used winding rules for polygon filling are 66 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 67 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 68 | //see http://glprogramming.com/red/chapter11.html 69 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 70 | 71 | #ifdef use_int32 72 | typedef int cInt; 73 | static cInt const loRange = 0x7FFF; 74 | static cInt const hiRange = 0x7FFF; 75 | #else 76 | typedef signed long long cInt; 77 | static cInt const loRange = 0x3FFFFFFF; 78 | static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 79 | typedef signed long long long64; //used by Int128 class 80 | typedef unsigned long long ulong64; 81 | 82 | #endif 83 | 84 | struct IntPoint { 85 | cInt X; 86 | cInt Y; 87 | #ifdef use_xyz 88 | cInt Z; 89 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 90 | #else 91 | IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 92 | #endif 93 | 94 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 95 | { 96 | return a.X == b.X && a.Y == b.Y; 97 | } 98 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 99 | { 100 | return a.X != b.X || a.Y != b.Y; 101 | } 102 | }; 103 | //------------------------------------------------------------------------------ 104 | 105 | typedef std::vector< IntPoint > Path; 106 | typedef std::vector< Path > Paths; 107 | 108 | inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} 109 | inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} 110 | 111 | std::ostream& operator <<(std::ostream &s, const IntPoint &p); 112 | std::ostream& operator <<(std::ostream &s, const Path &p); 113 | std::ostream& operator <<(std::ostream &s, const Paths &p); 114 | 115 | struct DoublePoint 116 | { 117 | double X; 118 | double Y; 119 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 120 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 121 | }; 122 | //------------------------------------------------------------------------------ 123 | 124 | #ifdef use_xyz 125 | typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 126 | #endif 127 | 128 | enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 129 | enum JoinType {jtSquare, jtRound, jtMiter}; 130 | enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 131 | 132 | class PolyNode; 133 | typedef std::vector< PolyNode* > PolyNodes; 134 | 135 | class PolyNode 136 | { 137 | public: 138 | PolyNode(); 139 | virtual ~PolyNode(){}; 140 | Path Contour; 141 | PolyNodes Childs; 142 | PolyNode* Parent; 143 | PolyNode* GetNext() const; 144 | bool IsHole() const; 145 | bool IsOpen() const; 146 | int ChildCount() const; 147 | private: 148 | unsigned Index; //node index in Parent.Childs 149 | bool m_IsOpen; 150 | JoinType m_jointype; 151 | EndType m_endtype; 152 | PolyNode* GetNextSiblingUp() const; 153 | void AddChild(PolyNode& child); 154 | friend class Clipper; //to access Index 155 | friend class ClipperOffset; 156 | }; 157 | 158 | class PolyTree: public PolyNode 159 | { 160 | public: 161 | ~PolyTree(){Clear();}; 162 | PolyNode* GetFirst() const; 163 | void Clear(); 164 | int Total() const; 165 | private: 166 | PolyNodes AllNodes; 167 | friend class Clipper; //to access AllNodes 168 | }; 169 | 170 | bool Orientation(const Path &poly); 171 | double Area(const Path &poly); 172 | int PointInPolygon(const IntPoint &pt, const Path &path); 173 | 174 | void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 175 | void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 176 | void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); 177 | 178 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 179 | void CleanPolygon(Path& poly, double distance = 1.415); 180 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 181 | void CleanPolygons(Paths& polys, double distance = 1.415); 182 | 183 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); 184 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); 185 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); 186 | 187 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 188 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 189 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 190 | 191 | void ReversePath(Path& p); 192 | void ReversePaths(Paths& p); 193 | 194 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 195 | 196 | //enums that are used internally ... 197 | enum EdgeSide { esLeft = 1, esRight = 2}; 198 | 199 | //forward declarations (for stuff used internally) ... 200 | struct TEdge; 201 | struct IntersectNode; 202 | struct LocalMinimum; 203 | struct Scanbeam; 204 | struct OutPt; 205 | struct OutRec; 206 | struct Join; 207 | 208 | typedef std::vector < OutRec* > PolyOutList; 209 | typedef std::vector < TEdge* > EdgeList; 210 | typedef std::vector < Join* > JoinList; 211 | typedef std::vector < IntersectNode* > IntersectList; 212 | 213 | //------------------------------------------------------------------------------ 214 | 215 | //ClipperBase is the ancestor to the Clipper class. It should not be 216 | //instantiated directly. This class simply abstracts the conversion of sets of 217 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 218 | class ClipperBase 219 | { 220 | public: 221 | ClipperBase(); 222 | virtual ~ClipperBase(); 223 | bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); 224 | bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); 225 | virtual void Clear(); 226 | IntRect GetBounds(); 227 | bool PreserveCollinear() {return m_PreserveCollinear;}; 228 | void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; 229 | protected: 230 | void DisposeLocalMinimaList(); 231 | TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); 232 | void PopLocalMinima(); 233 | virtual void Reset(); 234 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 235 | void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); 236 | TEdge* DescendToMin(TEdge *&E); 237 | void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); 238 | 239 | typedef std::vector MinimaList; 240 | MinimaList::iterator m_CurrentLM; 241 | MinimaList m_MinimaList; 242 | 243 | bool m_UseFullRange; 244 | EdgeList m_edges; 245 | bool m_PreserveCollinear; 246 | bool m_HasOpenPaths; 247 | }; 248 | //------------------------------------------------------------------------------ 249 | 250 | class Clipper : public virtual ClipperBase 251 | { 252 | public: 253 | Clipper(int initOptions = 0); 254 | ~Clipper(); 255 | bool Execute(ClipType clipType, 256 | Paths &solution, 257 | PolyFillType subjFillType = pftEvenOdd, 258 | PolyFillType clipFillType = pftEvenOdd); 259 | bool Execute(ClipType clipType, 260 | PolyTree &polytree, 261 | PolyFillType subjFillType = pftEvenOdd, 262 | PolyFillType clipFillType = pftEvenOdd); 263 | bool ReverseSolution() {return m_ReverseOutput;}; 264 | void ReverseSolution(bool value) {m_ReverseOutput = value;}; 265 | bool StrictlySimple() {return m_StrictSimple;}; 266 | void StrictlySimple(bool value) {m_StrictSimple = value;}; 267 | //set the callback function for z value filling on intersections (otherwise Z is 0) 268 | #ifdef use_xyz 269 | void ZFillFunction(ZFillCallback zFillFunc); 270 | #endif 271 | protected: 272 | void Reset(); 273 | virtual bool ExecuteInternal(); 274 | private: 275 | PolyOutList m_PolyOuts; 276 | JoinList m_Joins; 277 | JoinList m_GhostJoins; 278 | IntersectList m_IntersectList; 279 | ClipType m_ClipType; 280 | typedef std::priority_queue ScanbeamList; 281 | ScanbeamList m_Scanbeam; 282 | TEdge *m_ActiveEdges; 283 | TEdge *m_SortedEdges; 284 | bool m_ExecuteLocked; 285 | PolyFillType m_ClipFillType; 286 | PolyFillType m_SubjFillType; 287 | bool m_ReverseOutput; 288 | bool m_UsingPolyTree; 289 | bool m_StrictSimple; 290 | #ifdef use_xyz 291 | ZFillCallback m_ZFill; //custom callback 292 | #endif 293 | void SetWindingCount(TEdge& edge); 294 | bool IsEvenOddFillType(const TEdge& edge) const; 295 | bool IsEvenOddAltFillType(const TEdge& edge) const; 296 | void InsertScanbeam(const cInt Y); 297 | cInt PopScanbeam(); 298 | void InsertLocalMinimaIntoAEL(const cInt botY); 299 | void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); 300 | void AddEdgeToSEL(TEdge *edge); 301 | void CopyAELToSEL(); 302 | void DeleteFromSEL(TEdge *e); 303 | void DeleteFromAEL(TEdge *e); 304 | void UpdateEdgeIntoAEL(TEdge *&e); 305 | void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); 306 | bool IsContributing(const TEdge& edge) const; 307 | bool IsTopHorz(const cInt XPos); 308 | void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); 309 | void DoMaxima(TEdge *e); 310 | void ProcessHorizontals(bool IsTopOfScanbeam); 311 | void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); 312 | void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 313 | OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 314 | OutRec* GetOutRec(int idx); 315 | void AppendPolygon(TEdge *e1, TEdge *e2); 316 | void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); 317 | OutRec* CreateOutRec(); 318 | OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 319 | void DisposeAllOutRecs(); 320 | void DisposeOutRec(PolyOutList::size_type index); 321 | bool ProcessIntersections(const cInt botY, const cInt topY); 322 | void BuildIntersectList(const cInt botY, const cInt topY); 323 | void ProcessIntersectList(); 324 | void ProcessEdgesAtTopOfScanbeam(const cInt topY); 325 | void BuildResult(Paths& polys); 326 | void BuildResult2(PolyTree& polytree); 327 | void SetHoleState(TEdge *e, OutRec *outrec); 328 | void DisposeIntersectNodes(); 329 | bool FixupIntersectionOrder(); 330 | void FixupOutPolygon(OutRec &outrec); 331 | bool IsHole(TEdge *e); 332 | bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); 333 | void FixHoleLinkage(OutRec &outrec); 334 | void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); 335 | void ClearJoins(); 336 | void ClearGhostJoins(); 337 | void AddGhostJoin(OutPt *op, const IntPoint offPt); 338 | bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); 339 | void JoinCommonEdges(); 340 | void DoSimplePolygons(); 341 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 342 | void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec); 343 | #ifdef use_xyz 344 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 345 | #endif 346 | }; 347 | //------------------------------------------------------------------------------ 348 | 349 | class ClipperOffset 350 | { 351 | public: 352 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 353 | ~ClipperOffset(); 354 | void AddPath(const Path& path, JoinType joinType, EndType endType); 355 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 356 | void Execute(Paths& solution, double delta); 357 | void Execute(PolyTree& solution, double delta); 358 | void Clear(); 359 | double MiterLimit; 360 | double ArcTolerance; 361 | private: 362 | Paths m_destPolys; 363 | Path m_srcPoly; 364 | Path m_destPoly; 365 | std::vector m_normals; 366 | double m_delta, m_sinA, m_sin, m_cos; 367 | double m_miterLim, m_StepsPerRad; 368 | IntPoint m_lowest; 369 | PolyNode m_polyNodes; 370 | 371 | void FixOrientations(); 372 | void DoOffset(double delta); 373 | void OffsetPoint(int j, int& k, JoinType jointype); 374 | void DoSquare(int j, int k); 375 | void DoMiter(int j, int k, double r); 376 | void DoRound(int j, int k); 377 | }; 378 | //------------------------------------------------------------------------------ 379 | 380 | class clipperException : public std::exception 381 | { 382 | public: 383 | clipperException(const char* description): m_descr(description) {} 384 | virtual ~clipperException() throw() {} 385 | virtual const char* what() const throw() {return m_descr.c_str();} 386 | private: 387 | std::string m_descr; 388 | }; 389 | //------------------------------------------------------------------------------ 390 | 391 | } //ClipperLib namespace 392 | 393 | #endif //clipper_hpp 394 | 395 | 396 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libarea (0.0.20140514-1) unstable; urgency=low 2 | 3 | New upstream version: 4 | * (removed) debian patches are now applied upstream 5 | 6 | -- Romuald Conty Mon, 14 Apr 2014 14:42:42 +0200 7 | 8 | libarea (0.0.20130701-2) unstable; urgency=low 9 | 10 | * Split into 3 packages: libarea0, libarea-dev and python-area 11 | 12 | -- Romuald Conty Tue, 10 Apr 2014 22:42:42 +0200 13 | 14 | libarea (0.0.20130701-1) unstable; urgency=low 15 | 16 | * Use CMake instead of Makefile 17 | 18 | -- Romuald Conty Tue, 08 Apr 2014 15:42:42 +0200 19 | 20 | libarea (0.0.2) karmic; urgency=low 21 | 22 | * Fix some minor debian package issues. 23 | 24 | -- Sebastian Kuzminsky Thu, 18 Mar 2010 13:10:07 -0600 25 | 26 | libarea (0.0.1-svn0) unstable; urgency=low 27 | 28 | * Initial release 29 | 30 | -- Joachim Steiger Tue, 05 May 2009 15:57:02 +0200 31 | 32 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libarea 2 | Priority: extra 3 | Section: misc 4 | Maintainer: Romuald Conty 5 | Build-Depends: debhelper (>= 7), libboost-python-dev, cmake, python-dev (>=2.6.5) 6 | Standards-Version: 3.9.3 7 | Homepage: https://code.google.com/p/libarea/ 8 | Vcs-Svn: http://libarea.googlecode.com/svn/trunk/ 9 | Vcs-Browser: https://code.google.com/p/libarea/source/browse/ 10 | 11 | Package: libarea0 12 | Architecture: any 13 | Section: libs 14 | Depends: ${shlibs:Depends}, ${misc:Depends} 15 | Description: Area C++ library 16 | Area is a CAM-related software for pocketing operation. 17 | . 18 | Derived from the kbool library written by Klaas Holwerda 19 | http://boolean.klaasholwerda.nl/bool.html. 20 | . 21 | This library uses "Clipper" implementation. 22 | . 23 | This package contains the shared library. 24 | 25 | Package: libarea-dev 26 | Architecture: all 27 | Section: libdevel 28 | Depends: ${misc:Depends}, libarea0 (= ${binary:Version}) 29 | Description: Area C++ library (development files) 30 | Derived from the kbool library written by Klaas Holwerda 31 | http://boolean.klaasholwerda.nl/bool.html. 32 | . 33 | This package contains the development files (ie. headers). 34 | 35 | Package: python-area 36 | Architecture: any 37 | Section: python 38 | Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} 39 | Provides: ${python:Provides} 40 | Description: Python module for libarea 41 | Area is a CAM-related software for pocketing operation. 42 | . 43 | Derived from the kbool library written by Klaas Holwerda 44 | http://boolean.klaasholwerda.nl/bool.html. 45 | . 46 | This module uses "Boolean" implementation. 47 | . 48 | This package contains the python module. 49 | 50 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: heekscad 3 | Source: 4 | 5 | Files: * 6 | Copyright: 2008-2014 Dan Heeks 7 | 8 | License: BSD 3-Clause License 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | * Neither the name of the HeeksCAD nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | . 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | Files: debian/* 33 | Copyright: 2013-2014 Romuald Conty 34 | 2009 Joachim Steiger 35 | License: GPL-2+ 36 | This package is free software; you can redistribute it and/or modify 37 | it under the terms of the GNU General Public License as published by 38 | the Free Software Foundation; either version 2 of the License, or 39 | (at your option) any later version. 40 | . 41 | This package is distributed in the hope that it will be useful, 42 | but WITHOUT ANY WARRANTY; without even the implied warranty of 43 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 44 | GNU General Public License for more details. 45 | . 46 | You should have received a copy of the GNU General Public License 47 | along with this program. If not, see 48 | . 49 | On Debian systems, the complete text of the GNU General 50 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". -------------------------------------------------------------------------------- /debian/libarea-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/area/Arc.h 2 | usr/include/area/Area.h 3 | usr/include/area/AreaDxf.h 4 | usr/include/area/AreaOrderer.h 5 | usr/include/area/Box2D.h 6 | usr/include/area/Circle.h 7 | usr/include/area/Curve.h 8 | usr/include/area/Point.h 9 | usr/include/area/dxf.h 10 | usr/include/area/kurve/geometry.h 11 | usr/lib/libarea.so 12 | -------------------------------------------------------------------------------- /debian/libarea0.install: -------------------------------------------------------------------------------- 1 | usr/lib/libarea.so.* 2 | -------------------------------------------------------------------------------- /debian/python-area.install: -------------------------------------------------------------------------------- 1 | usr/lib/python2* 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | #export DH_VERBOSE=1 6 | 7 | # "nocheck" option prevents from running tests (which fails... FIXME) 8 | #export DEB_BUILD_OPTIONS=nocheck 9 | 10 | %: 11 | dh $@ 12 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /dxf.h: -------------------------------------------------------------------------------- 1 | // dxf.h 2 | // Copyright (c) 2009, Dan Heeks 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | //Following is required to be defined on Ubuntu with OCC 6.3.1 19 | #ifndef HAVE_IOSTREAM 20 | #define HAVE_IOSTREAM 21 | #endif 22 | 23 | typedef int Aci_t; // AutoCAD color index 24 | 25 | typedef enum 26 | { 27 | eUnspecified = 0, // Unspecified (No units) 28 | eInches, 29 | eFeet, 30 | eMiles, 31 | eMillimeters, 32 | eCentimeters, 33 | eMeters, 34 | eKilometers, 35 | eMicroinches, 36 | eMils, 37 | eYards, 38 | eAngstroms, 39 | eNanometers, 40 | eMicrons, 41 | eDecimeters, 42 | eDekameters, 43 | eHectometers, 44 | eGigameters, 45 | eAstronomicalUnits, 46 | eLightYears, 47 | eParsecs 48 | } eDxfUnits_t; 49 | 50 | 51 | struct SplineData 52 | { 53 | double norm[3]; 54 | int degree; 55 | int knots; 56 | int control_points; 57 | int fit_points; 58 | int flag; 59 | std::list starttanx; 60 | std::list starttany; 61 | std::list starttanz; 62 | std::list endtanx; 63 | std::list endtany; 64 | std::list endtanz; 65 | std::list knot; 66 | std::list weight; 67 | std::list controlx; 68 | std::list controly; 69 | std::list controlz; 70 | std::list fitx; 71 | std::list fity; 72 | std::list fitz; 73 | }; 74 | 75 | class CDxfWrite{ 76 | private: 77 | std::ofstream* m_ofs; 78 | bool m_fail; 79 | 80 | public: 81 | CDxfWrite(const char* filepath); 82 | ~CDxfWrite(); 83 | 84 | bool Failed(){return m_fail;} 85 | 86 | void WriteLine(const double* s, const double* e, const char* layer_name ); 87 | void WritePoint(const double*, const char*); 88 | void WriteArc(const double* s, const double* e, const double* c, bool dir, const char* layer_name ); 89 | void WriteEllipse(const double* c, double major_radius, double minor_radius, double rotation, double start_angle, double end_angle, bool dir, const char* layer_name ); 90 | void WriteCircle(const double* c, double radius, const char* layer_name ); 91 | }; 92 | 93 | // derive a class from this and implement it's virtual functions 94 | class CDxfRead{ 95 | private: 96 | std::ifstream* m_ifs; 97 | 98 | bool m_fail; 99 | char m_str[1024]; 100 | char m_unused_line[1024]; 101 | eDxfUnits_t m_eUnits; 102 | char m_layer_name[1024]; 103 | char m_section_name[1024]; 104 | char m_block_name[1024]; 105 | bool m_ignore_errors; 106 | 107 | 108 | typedef std::map< std::string,Aci_t > LayerAciMap_t; 109 | LayerAciMap_t m_layer_aci; // layer names -> layer color aci map 110 | 111 | bool ReadUnits(); 112 | bool ReadLayer(); 113 | bool ReadLine(); 114 | bool ReadText(); 115 | bool ReadArc(); 116 | bool ReadCircle(); 117 | bool ReadEllipse(); 118 | bool ReadPoint(); 119 | bool ReadSpline(); 120 | bool ReadLwPolyLine(); 121 | bool ReadPolyLine(); 122 | bool ReadVertex(double *pVertex, bool *bulge_found, double *bulge); 123 | void OnReadArc(double start_angle, double end_angle, double radius, const double* c); 124 | void OnReadCircle(const double* c, double radius); 125 | void OnReadEllipse(const double* c, const double* m, double ratio, double start_angle, double end_angle); 126 | 127 | void get_line(); 128 | void put_line(const char *value); 129 | void DerefACI(); 130 | 131 | protected: 132 | Aci_t m_aci; // manifest color name or 256 for layer color 133 | 134 | public: 135 | CDxfRead(const char* filepath); // this opens the file 136 | ~CDxfRead(); // this closes the file 137 | 138 | bool Failed(){return m_fail;} 139 | void DoRead(const bool ignore_errors = false); // this reads the file and calls the following functions 140 | 141 | double mm( const double & value ) const; 142 | 143 | bool IgnoreErrors() const { return(m_ignore_errors); } 144 | 145 | virtual void OnReadLine(const double* s, const double* e){} 146 | virtual void OnReadPoint(const double* s){} 147 | virtual void OnReadText(const double* point, const double height, const char* text){} 148 | virtual void OnReadArc(const double* s, const double* e, const double* c, bool dir){} 149 | virtual void OnReadCircle(const double* s, const double* c, bool dir){} 150 | virtual void OnReadEllipse(const double* c, double major_radius, double minor_radius, double rotation, double start_angle, double end_angle, bool dir){} 151 | virtual void OnReadSpline(struct SplineData& sd){} 152 | virtual void AddGraphics() const { } 153 | 154 | std::string LayerName() const; 155 | 156 | }; 157 | -------------------------------------------------------------------------------- /kurve/Finite.cpp: -------------------------------------------------------------------------------- 1 | // written by g.j.hawkesford 2006 for Camtek Gmbh 2 | // 3 | // This program is released under the BSD license. See the file COPYING for details. 4 | // 5 | 6 | #include "geometry.h" 7 | 8 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | // finite intersections 10 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | #ifndef WIN32 13 | #define __min(a,b) ((ab)?a:b) 15 | #endif 16 | 17 | namespace geoff_geometry { 18 | int Intof(const Span& sp0, const Span& sp1, Point& p0, Point& p1, double t[4]) 19 | { 20 | // returns the number of intersects (lying within spans sp0, sp1) 21 | if(sp0.box.outside(sp1.box) == true) return 0; 22 | if(!sp0.dir) { 23 | if(!sp1.dir) { 24 | // line line 25 | return LineLineIntof(sp0, sp1, p0, t); 26 | } 27 | else { 28 | // line arc 29 | return LineArcIntof(sp0, sp1, p0, p1, t); 30 | } 31 | } 32 | else { 33 | if(!sp1.dir) { 34 | // arc line 35 | return LineArcIntof(sp1, sp0, p0, p1, t); 36 | } 37 | else { 38 | // arc arc 39 | return ArcArcIntof(sp0, sp1, p0, p1, t); 40 | } 41 | } 42 | } 43 | 44 | int LineLineIntof(const Span& sp0 , const Span& sp1, Point& p, double t[2]) { 45 | // intersection between 2 Line2d 46 | // returns 0 for no intersection in range of either span 47 | // returns 1 for intersction in range of both spans 48 | // t[0] is parameter on sp0, 49 | // t[1] is parameter on sp1 50 | Vector2d v0(sp0.p0, sp0.p1); 51 | Vector2d v1(sp1.p0, sp1.p1); 52 | Vector2d v2(sp0.p0, sp1.p0); 53 | 54 | double cp = v1 ^ v0; 55 | 56 | if(fabs(cp) < UNIT_VECTOR_TOLERANCE ) { 57 | p = INVALID_POINT; 58 | return 0; // parallel or degenerate lines 59 | } 60 | 61 | t[0] = (v1 ^ v2) / cp; 62 | p = v0 * t[0] + sp0.p0; 63 | p.ok = true; 64 | double toler = geoff_geometry::TOLERANCE / sp0.length; // calc a parametric tolerance 65 | 66 | t[1] = (v0 ^ v2) / cp; 67 | if(t[0] < -toler || t[0] > 1 + toler) return 0; // intersection on first? 68 | toler = geoff_geometry::TOLERANCE / sp1.length; // calc a parametric tolerance 69 | if(t[1] < -toler || t[1] > 1 + toler) return 0; // intersection on second? 70 | return 1; 71 | } 72 | 73 | int LineArcIntof(const Span& line, const Span& arc, Point& p0, Point& p1, double t[4]) { 74 | // inters of line arc 75 | // solving x = x0 + dx * t x = y0 + dy * t 76 | // x = xc + R * cos(a) y = yc + R * sin(a) for t 77 | // gives :- t² (dx² + dy²) + 2t(dx*dx0 + dy*dy0) + (x0-xc)² + (y0-yc)² - R² = 0 78 | int nRoots; 79 | Vector2d v0(arc.pc, line.p0); 80 | Vector2d v1(line.p0, line.p1); 81 | double s = v1.magnitudesqd(); 82 | 83 | p0.ok = p1.ok = false; 84 | if((nRoots = quadratic(s, 2 * (v0 * v1), v0.magnitudesqd() - arc.radius * arc.radius, t[0], t[1])) != 0) { 85 | double toler = geoff_geometry::TOLERANCE / sqrt(s); // calc a parametric tolerance 86 | if(t[0] > -toler && t[0] < 1 + toler) { 87 | p0 = v1 * t[0] + line.p0; 88 | p0.ok = arc.OnSpan(p0, &t[2]); 89 | } 90 | if(nRoots == 2) { 91 | if(t[1] > -toler && t[1] < 1 + toler) { 92 | p1 = v1 * t[1] + line.p0; 93 | p1.ok = arc.OnSpan(p1, &t[3]); 94 | } 95 | } 96 | if(!p0.ok && p1.ok) { 97 | p0 = p1; 98 | p1.ok = false; 99 | } 100 | nRoots = (int)p0.ok + (int)p1.ok; 101 | } 102 | return nRoots; 103 | } 104 | 105 | int ArcArcIntof(const Span& arc0, const Span& arc1, Point& pLeft, Point& pRight, double t[4]) { 106 | // Intof 2 arcs 107 | int numInts = Intof(Circle(arc0.pc, arc0.radius), Circle(arc1.pc, arc1.radius), pLeft, pRight); 108 | 109 | if(numInts == 0) { 110 | pLeft = arc0.p1; 111 | pLeft.ok = false; 112 | return 0; 113 | } 114 | int nLeft = arc0.OnSpan(pLeft) && arc1.OnSpan(pLeft); 115 | int nRight = (numInts == 2)?arc0.OnSpan(pRight) && arc1.OnSpan(pRight) : 0; 116 | if(nLeft == 0 && nRight) pLeft = pRight; 117 | return nLeft + nRight; 118 | } 119 | 120 | bool Span::OnSpan(const Point& p)const { 121 | double t; 122 | return OnSpan(p, &t); 123 | } 124 | 125 | bool Span::OnSpan(const Point& p, double* t)const { 126 | // FAST OnSpan test - assumes that p lies ON the unbounded span 127 | #if _DEBUG 128 | if(this->returnSpanProperties == false) { 129 | FAILURE(L"OnSpan - properties no set, incorrect calling code"); 130 | } 131 | #endif 132 | #if 0 133 | if(NullSpan) { 134 | *t = 0.0; 135 | return (p == p0); 136 | } 137 | 138 | if(p == p0) { 139 | *t = 0.0; 140 | return true; 141 | } 142 | 143 | if(p == p1) { 144 | *t = 1.0; 145 | return true; 146 | } 147 | #endif 148 | bool ret; 149 | // if(p == this->p0 || p == this->p1) return true; 150 | 151 | if(dir == LINEAR) { 152 | #if 1 153 | #if _DEBUG 154 | // check p is on line 155 | CLine cl(*this); 156 | double d = fabs(cl.Dist(p)); 157 | if( d > geoff_geometry::TOLERANCE) { 158 | FAILURE(L"OnSpan - point not on linear span, incorrect calling code"); 159 | } 160 | #endif 161 | #endif 162 | Vector2d v0(p0, p); 163 | *t = vs * v0; 164 | // ret = (*t > - geoff_geometry::TOLERANCE && *t < length + geoff_geometry::TOLERANCE); 165 | 166 | *t = *t / length; 167 | ret = (*t >= 0 && *t <= 1.0 ); 168 | 169 | } 170 | else { 171 | // true if p lies on arc span sp (p must be on circle of span) 172 | #if 1 173 | #if _DEBUG 174 | // check that p lies on the arc 175 | double d = p.Dist(pc); 176 | if(FNE(d, radius, geoff_geometry::TOLERANCE)) { 177 | FAILURE(L"OnSpan - point not on circular span, incorrect calling code"); 178 | } 179 | 180 | #endif 181 | #endif 182 | #if 0 // alt method (faster, but doesn't provide t) 183 | Vector2d v0(p0, p); 184 | Vector2d v1(p0, p1); 185 | 186 | // check angle to point from start 187 | double cp; 188 | ret = ((cp = (dir * (v0 ^ v1))) > 0); 189 | *t = 0.0;// incorrect !!! 190 | #else 191 | Vector2d v = ~Vector2d(pc, p); 192 | v.normalise(); 193 | if(dir == CW) v = -v; 194 | 195 | double ang = IncludedAngle(vs, v, dir); 196 | *t = ang / angle; 197 | ret = (*t >= 0 && *t <= 1.0); 198 | #endif 199 | } 200 | 201 | return ret; 202 | } 203 | 204 | Line::Line(const Point3d& p, const Vector3d& v0, bool boxed){ 205 | // constructor from point & vector 206 | p0 = p; 207 | v = v0; 208 | length = v.magnitude(); 209 | if(boxed) minmax(); 210 | ok = (length > geoff_geometry::TOLERANCE); 211 | } 212 | 213 | Line::Line(const Point3d& p, const Point3d& p1){ 214 | // constructor from 2 points 215 | p0 = p; 216 | v = Vector3d(p, p1); 217 | length = v.magnitude(); 218 | minmax(); 219 | ok = (length > geoff_geometry::TOLERANCE); 220 | } 221 | 222 | Line::Line(const Span& sp){ 223 | // contructor from linear span 224 | p0 = sp.p0; 225 | v = sp.vs * sp.length; 226 | length = sp.length; 227 | // box = sp.box; 228 | box.min = Point3d(sp.box.min); 229 | box.max = Point3d(sp.box.max); 230 | ok = !sp.NullSpan; 231 | } 232 | 233 | void Line::minmax() { 234 | MinMax(this->p0, box.min, box.max); 235 | MinMax(this->v + this->p0, box.min, box.max); 236 | } 237 | 238 | bool Line::atZ(double z, Point3d& p)const { 239 | // returns p at z on line 240 | if(FEQZ(this->v.getz())) return false; 241 | double t = (z - this->p0.z) / this->v.getz(); 242 | p = Point3d(this->p0.x + t * this->v.getx(), this->p0.y + t * this->v.gety(), z); 243 | return true; 244 | } 245 | 246 | 247 | bool Line::Shortest(const Line& l2, Line& lshort, double& t1, double& t2)const { 248 | /* 249 | Calculate the line segment PaPb that is the shortest route between 250 | two lines P1P2 and P3P4. Calculate also the values of mua and mub where 251 | Pa = P1 + t1 (P2 - P1) 252 | Pb = P3 + t2 (P4 - P3) 253 | Return FALSE if no solution exists. P Bourke method. 254 | Input this 1st line 255 | Input l2 2nd line 256 | Output lshort shortest line between lines (if lshort.ok == false, the line intersect at a point lshort.p0) 257 | Output t1 parameter at intersection on 1st Line 258 | Output t2 parameter at intersection on 2nd Line 259 | 260 | */ 261 | Vector3d v13(l2.p0, this->p0); 262 | if(this->ok == false || l2.ok == false) return false; 263 | 264 | double d1343 = v13 * l2.v; // dot products 265 | double d4321 = l2.v * this->v; 266 | double d1321 = v13 * this->v; 267 | double d4343 = l2.v * l2.v; 268 | double d2121 = this->v * this->v; 269 | 270 | double denom = d2121 * d4343 - d4321 * d4321; 271 | if(fabs(denom) < 1.0e-09) return false; 272 | double numer = d1343 * d4321 - d1321 * d4343; 273 | 274 | t1 = numer / denom; 275 | t2 = (d1343 + d4321 * t1) / d4343; 276 | 277 | lshort = Line(t1* this->v + this->p0, t2 * l2.v + l2.p0); 278 | t1 *= this->length; 279 | t2 *= l2.length; // parameter in line length for tolerance checking 280 | return true; 281 | } 282 | 283 | int Intof(const Line& l0, const Line& l1, Point3d& intof) 284 | { 285 | /* intersection of 2 vectors 286 | returns 0 for intercept but not within either vector 287 | returns 1 for intercept on both vectors 288 | 289 | note that this routine always returns 0 for parallel vectors 290 | method: 291 | x = x0 + dx0 * t0 for l0 292 | ... 293 | ... 294 | x = x1 + dx1 * t1 for l1 295 | ... 296 | ... 297 | 298 | x0 + dx0 * t0 = x1 + dx1 * t1 299 | dx0 * t0 - dx1 * t1 + x0 - x1 = 0 300 | 301 | setup 3 x 3 determinent for 302 | a0 t0 + b0 t1 + c0 = 0 303 | a1 t0 + b1 t1 + c1 = 0 304 | a2 t0 + b2 t1 + c2 = 0 305 | 306 | from above a = l0.v 307 | b = -l1.v 308 | c = Vector3d(l1, l0) 309 | */ 310 | // Vector3d a = l0.v; 311 | if(l0.box.outside(l1.box) == true) return 0; 312 | Vector3d b = -l1.v; 313 | Vector3d c = Vector3d(l1.p0, l0.p0); 314 | Vector3d det = l0.v ^ b; 315 | Vector3d t = b ^ c; 316 | 317 | // choose largest determinant & corresponding parameter for accuracy 318 | double t0 = t.getx(); 319 | double d = det.getx(); 320 | 321 | if(fabs(det.getz()) > fabs(det.gety())) { 322 | if(fabs(det.getz()) > fabs(det.getx())) { 323 | t0 = t.getz(); 324 | d = det.getz(); 325 | } 326 | } 327 | else { 328 | if(fabs(det.gety()) > fabs(det.getx())) { 329 | t0 = t.gety(); 330 | d = det.gety(); 331 | } 332 | } 333 | 334 | if(fabs(d) < 1.0e-06) return 0; 335 | 336 | t0 /= d; 337 | intof = l0.v * t0 + l0.p0; 338 | 339 | Point3d other; 340 | double t1; 341 | if(Dist(l1, intof, other, t1) > geoff_geometry::TOLERANCE) return 0; 342 | 343 | t0 *= l0.length; 344 | if( t0 < -geoff_geometry::TOLERANCE || t0 > l0.length + geoff_geometry::TOLERANCE || t1 < -geoff_geometry::TOLERANCE || t1 > l1.length + geoff_geometry::TOLERANCE ) return 0; 345 | return 1; 346 | } 347 | 348 | 349 | double Dist(const Line& l, const Point3d& p, Point3d& pnear, double& t){ 350 | // returns the distance of a point from a line and the near point on the extended line and the parameter of the near point (0-length) in range 351 | pnear = Near(l, p, t ); 352 | return p.Dist(pnear); 353 | } 354 | 355 | Point3d Near(const Line& l, const Point3d& p, double& t){ 356 | // returns the near point from a line on the extended line and the parameter of the near point (0-length) in range 357 | t = (Vector3d(l.p0, p) * l.v) / l.length; // t parametised 0 - line length 358 | return l.v * (t / l.length) + l.p0; 359 | } 360 | 361 | Point3d Line::Near(const Point3d& p, double& t)const{ 362 | // returns the near point from a line on the extended line and the parameter of the near point (0-length) in range 363 | t = (Vector3d(this->p0, p) * this->v) / this->length; // t parametised 0 - line length 364 | return this->v * (t / this->length) + this->p0; 365 | } 366 | 367 | double DistSq(const Point3d *p, const Vector3d *vl, const Point3d *pf) { 368 | /// returns the distance squared of pf from the line given by p,vl 369 | /// vl must be normalised 370 | Vector3d v(*p, *pf); 371 | Vector3d vcp = *vl ^ v; 372 | double d = vcp.magnitudeSq(); // l * sina 373 | return d; 374 | } 375 | 376 | double Dist(const Point3d *p, const Vector3d *vl, const Point3d *pf) { 377 | /// returns the distance of pf from the line given by p,vl 378 | /// vl must be normalised 379 | Vector3d v(*p, *pf); 380 | Vector3d vcp = *vl ^ v; 381 | double d = vcp.magnitude(); // l * sina 382 | return d; 383 | #if 0 384 | // slower method requires 2 sqrts 385 | Vector3d v(*p, *pf); 386 | double magv = v.normalise(); 387 | Vector3d cp = *vl ^ v; 388 | double d = magv * cp.magnitude(); 389 | return d; // l * sina 390 | #endif 391 | } 392 | 393 | double Dist(const Span& sp, const Point& p , Point& pnear ) { 394 | // returns distance of p from span, pnear is the nearpoint on the span (or endpoint) 395 | if(!sp.dir) { 396 | double d, t; 397 | Point3d unused_pnear; 398 | d = Dist(Line(sp), Point3d(p), unused_pnear, t); 399 | if(t < -geoff_geometry::TOLERANCE) { 400 | pnear = sp.p0; // nearpoint 401 | d = pnear.Dist(p); 402 | } 403 | else if(t > sp.length + geoff_geometry::TOLERANCE) { 404 | pnear = sp.p1; 405 | d = pnear.Dist(p); 406 | } 407 | return d; 408 | } 409 | else { 410 | // put pnear on the circle 411 | double radiusp; 412 | Vector2d v(sp.pc, p); 413 | if((radiusp = v.magnitude()) < geoff_geometry::TOLERANCE) { 414 | // point specified on circle centre - use first point as near point 415 | pnear = sp.p0; // nearpoint 416 | return sp.radius; 417 | } 418 | else { 419 | pnear = v * (sp.radius / radiusp) + sp.pc; 420 | 421 | // check if projected point is on the arc 422 | if(sp.OnSpan(pnear)) return fabs(radiusp - sp.radius); 423 | // double h1 = pnear.x - sp.p0.x ; 424 | // double v1 = pnear.y - sp.p0.y ; 425 | // double h2 = sp.p1.x - pnear.x ; 426 | // double v2 = sp.p1.y - pnear.y ; 427 | // if ( sp.dir * ( h1 * v2 - h2 * v1 ) >= 0 )return fabs(radiusp - sp.radius); 428 | 429 | // point not on arc so calc nearest end-point 430 | double ndist = p.Dist(sp.p0); 431 | double dist = p.Dist(sp.p1); 432 | if(ndist >= dist) { 433 | // sp.p1 is near point 434 | pnear = sp.p1; 435 | return dist; 436 | } 437 | 438 | // sp.p0 is near point 439 | pnear = sp.p0; // nearpoint 440 | return ndist ; 441 | } 442 | } 443 | } 444 | 445 | bool OnSpan(const Span& sp, const Point& p) { 446 | Point nullPoint; 447 | return OnSpan(sp, p, false, nullPoint, nullPoint); 448 | } 449 | 450 | bool OnSpan(const Span& sp, const Point& p, bool nearPoints, Point& pNear, Point& pOnSpan) { 451 | // function returns true if pNear == pOnSpan 452 | // returns pNear & pOnSpan if nearPoints true 453 | // pNear (nearest on unbound span) 454 | // pOnSpan (nearest on finite span) 455 | if(sp.dir) { 456 | // arc 457 | if(fabs(p.Dist(sp.pc) - sp.radius) > geoff_geometry::TOLERANCE) { 458 | if(!nearPoints) return false; 459 | } 460 | 461 | pNear = On(Circle(sp.pc, sp.radius), p); 462 | 463 | if(sp.OnSpan(pNear)) { 464 | if(nearPoints) pOnSpan = pNear; 465 | return true; // near point is on arc - already calculated 466 | } 467 | 468 | // point not on arc return the nearest end-point 469 | if(nearPoints) pOnSpan = (p.Dist(sp.p0) >= p.Dist(sp.p1)) ?sp.p1 : sp.p0; 470 | return false; 471 | } 472 | else { 473 | // straight 474 | if(fabs(CLine(sp.p0, sp.vs).Dist(p)) > geoff_geometry::TOLERANCE) { 475 | if(!nearPoints) return false; 476 | } 477 | Vector2d v(sp.p0, p); 478 | double t = v * sp.vs; 479 | if(nearPoints) pNear = sp.vs * t + sp.p0; 480 | bool onSpan = (t > - geoff_geometry::TOLERANCE && t < sp.length + geoff_geometry::TOLERANCE); 481 | if(! onSpan) { 482 | if(nearPoints) pOnSpan = (p.Dist(sp.p0) >= p.Dist(sp.p1))?sp.p1 : sp.p0; 483 | } 484 | else { 485 | if(nearPoints) pOnSpan = pNear; 486 | } 487 | return onSpan; 488 | } 489 | } 490 | 491 | // Triangle3d Constructors 492 | Triangle3d::Triangle3d(const Point3d& p1, const Point3d& p2, const Point3d& p3) { 493 | vert1 = p1; 494 | vert2 = p2; 495 | vert3 = p3; 496 | v0 = Vector3d(vert1, vert2); 497 | v1 = Vector3d(vert1, vert3); 498 | ok = true; 499 | 500 | // set box 501 | box.min.x = __min(__min(vert1.x, vert2.x), vert3.x); 502 | box.min.y = __min(__min(vert1.y, vert2.y), vert3.y); 503 | box.min.z = __min(__min(vert1.z, vert2.z), vert3.z); 504 | 505 | box.max.x = __max(__max(vert1.x, vert2.x), vert3.x); 506 | box.max.y = __max(__max(vert1.y, vert2.y), vert3.y); 507 | box.max.z = __max(__max(vert1.z, vert2.z), vert3.z); 508 | } 509 | 510 | // Triangle3d methods 511 | bool Triangle3d::Intof(const Line& l, Point3d& intof)const { 512 | // returns intersection triangle to line in intof 513 | // funtion returns true for intersection, false for no intersection 514 | // method based on Möller & Trumbore(1997) (Barycentric coordinates) 515 | // based on incorrect Pseudo code from "Geometric Tools for Computer Graphics" p.487 516 | if(box.outside(l.box) == true) return false; 517 | 518 | Vector3d line(l.v); 519 | line.normalise(); 520 | 521 | Vector3d p = line ^ v1; // cross product 522 | double tmp = p * v0; // dot product 523 | 524 | if(FEQZ(tmp)) return false; 525 | 526 | tmp = 1 / tmp; 527 | Vector3d s(vert1, l.p0); 528 | 529 | double u = tmp * (s * p); // barycentric coordinate 530 | if(u < 0 || u > 1) return false; // not inside triangle 531 | 532 | Vector3d q = s ^ v0; 533 | double v = tmp * (line * q); // barycentric coordinate 534 | if(v < 0 || v > 1) return false; // not inside triangle 535 | 536 | if( u + v > 1) return false; // not inside triangle 537 | 538 | double t = tmp * (v1 * q); 539 | intof = line * t + l.p0; 540 | return true; 541 | } 542 | 543 | 544 | // box class 545 | bool Box::outside(const Box& b)const { 546 | // returns true if this box is outside b 547 | if(b.ok == false || this->ok == false) return false; // no box set 548 | if(this->max.x < b.min.x) return true; 549 | if(this->max.y < b.min.y) return true; 550 | if(this->min.x > b.max.x) return true; 551 | if(this->min.y > b.max.y) return true; 552 | return false; 553 | } 554 | 555 | void Box::combine(const Box& b) { 556 | if(b.max.x > this->max.x) this->max.x = b.max.x; 557 | if(b.max.y > this->max.y) this->max.y = b.max.y; 558 | if(b.min.x < this->min.x) this->min.x = b.min.x; 559 | if(b.min.y < this->min.y) this->min.y = b.min.y; 560 | } 561 | 562 | void Box3d::combine(const Box3d& b) { 563 | if(b.max.x > this->max.x) this->max.x = b.max.x; 564 | if(b.max.y > this->max.y) this->max.y = b.max.y; 565 | if(b.max.z > this->max.z) this->max.z = b.max.z; 566 | if(b.min.x < this->min.x) this->min.x = b.min.x; 567 | if(b.min.y < this->min.y) this->min.y = b.min.y; 568 | if(b.min.z < this->min.z) this->min.z = b.min.z; 569 | } 570 | 571 | bool Box3d::outside(const Box3d& b) const{ 572 | // returns true if this box is outside b 573 | if(b.ok == false || this->ok == false) return false; // no box set 574 | if(this->max.x < b.min.x) return true; 575 | if(this->max.y < b.min.y) return true; 576 | if(this->max.z < b.min.z) return true; 577 | if(this->min.x > b.max.x) return true; 578 | if(this->min.y > b.max.y) return true; 579 | if(this->min.z > b.max.z) return true; 580 | return false; 581 | } 582 | #if 0 583 | Span3d IsPtsSpan3d(const double* a, int n, double tolerance, double* deviation) { 584 | // returns a span3d if all points are within tolerance 585 | int np = n / 3; // number of points 586 | if(np < 2) return Span3d(); // Invalid span3d 587 | Point3d sp = Point3d(&a[0]); 588 | Point3d ep = Point3d(&a[n-3]); 589 | Line line = IsPtsLine(a, n, tolerance, deviation); 590 | if(line.ok) return Span3d(sp, ep); // it's a line 591 | 592 | *deviation = 0; // cumulative deviation 593 | Point3d mp = Point3d(&a[np / 2 * 3]); // mid point 594 | Plane plane(sp, mp, ep); 595 | if(plane.ok) { 596 | // plane of the arc is ok 597 | // calculate centre point 598 | Vector3d vs(mp, sp); 599 | vs.normalise(); 600 | Vector3d ve(mp, ep); 601 | ve.normalise(); 602 | Vector3d rs = vs ^ plane.normal; 603 | Vector3d re = ve ^ plane.normal; 604 | 605 | Line rsl(sp.Mid(mp), rs, false); 606 | Line rel(ep.Mid(mp), re, false); 607 | 608 | Point3d pc; 609 | Intof(rsl, rel, pc); 610 | double radius = pc.Dist(sp); 611 | 612 | // check other points on circle 613 | for(int i = 2; i < np - 1; i++) { 614 | Point3d p(&a[i*3]); 615 | double dp = fabs(plane.Dist(p)); 616 | double dr = fabs(p.Dist(pc) - radius); 617 | double tolerance = 10.0 * 1.0e-6; 618 | if(dp > tolerance || dr > tolerance) { 619 | return Span3d(); 620 | } 621 | 622 | } 623 | return Span3d(CW, plane.normal, sp, ep, pc); 624 | 625 | } 626 | return Span3d(); 627 | } 628 | #endif 629 | 630 | Line IsPtsLine(const double* a, int n, double tolerance, double* deviation) { 631 | // returns a Line if all points are within tolerance 632 | // deviation is returned as the sum of all deviations of interior points to line(sp,ep) 633 | int np = n / 3; // number of points 634 | *deviation = 0; // cumulative deviation 635 | if(np < 2) return Line(); // Invalid line 636 | 637 | Point3d sp(&a[0]); 638 | Point3d ep(&a[n-3]); 639 | Line line(sp, ep); // line start - end 640 | 641 | if(line.ok) { 642 | for(int j = 1; j < np - 1; j++) { 643 | Point3d mp(&a[j * 3]); 644 | double t, d=0; 645 | if((d = mp.Dist(line.Near(mp, t))) > tolerance) { 646 | line.ok = false; 647 | return line; 648 | } 649 | *deviation = *deviation + d; 650 | } 651 | } 652 | return line; 653 | } 654 | } 655 | -------------------------------------------------------------------------------- /kurve/Matrix.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3d geometry classes - implements some 3d stuff 3 | // 4 | // g.j.hawkesford August 2003 5 | // 6 | // This program is released under the BSD license. See the file COPYING for details. 7 | // 8 | //////////////////////////////////////////////////////////////////////////////////////////////// 9 | 10 | #include "geometry.h" 11 | using namespace geoff_geometry; 12 | 13 | #ifdef PEPSDLL 14 | #include "vdm.h" 15 | #include "pepsdll.h" 16 | #include "realds.h" 17 | #endif 18 | //////////////////////////////////////////////////////////////////////////////////////////////// 19 | // matrix 20 | //////////////////////////////////////////////////////////////////////////////////////////////// 21 | namespace geoff_geometry { 22 | 23 | Matrix::Matrix(){ 24 | Unit(); 25 | } 26 | Matrix::Matrix(double m[16]) { 27 | memcpy(e, m, sizeof(e)); 28 | this->IsUnit(); 29 | this->IsMirrored(); 30 | } 31 | 32 | Matrix::Matrix( const Matrix& m) 33 | { 34 | *this = m; 35 | } 36 | 37 | bool Matrix::operator==(const Matrix &m)const{ 38 | // m1 == m2 39 | if(this->m_unit != m.m_unit || this->m_mirrored != m.m_mirrored) return false; 40 | for(int i = 0; i < 16; i++) 41 | if(FEQ(this->e[i], m.e[i], TIGHT_TOLERANCE) == false) return false; 42 | return true; 43 | } 44 | 45 | #if 0 46 | const Matrix& Matrix::operator=( Matrix &m) { 47 | for(int i = 0; i < 16; i++) e[i] = m.e[i]; 48 | m_unit = m.m_unit; 49 | m_mirrored = m.m_mirrored; 50 | return *this; 51 | } 52 | #endif 53 | void Matrix::Unit() 54 | { 55 | // homogenous matrix - set as unit matrix 56 | memset(e, 0, sizeof(e)); 57 | e[0] = e[5] = e[10] = e[15] = 1; 58 | m_unit = true; 59 | m_mirrored = false; 60 | } 61 | 62 | void Matrix::Get(double* p) const 63 | { 64 | // copy the matrix 65 | memcpy(p, e, sizeof(e)); 66 | } 67 | void Matrix::Put(double* p) 68 | { 69 | // assign the matrix 70 | memcpy(e, p, sizeof(e)); 71 | m_unit = false; // don't know 72 | m_mirrored = -1; // don't know 73 | 74 | } 75 | void Matrix::Translate(double x, double y, double z) 76 | { 77 | // translation 78 | e[3] += x; 79 | e[7] += y; 80 | e[11] += z; 81 | m_unit = false; 82 | } 83 | 84 | void Matrix::Rotate(double angle, Vector3d *rotAxis) { 85 | /// Rotation about rotAxis with angle 86 | Rotate(sin(angle), cos(angle), rotAxis); 87 | } 88 | 89 | void Matrix::Rotate(double sinang, double cosang, Vector3d *rotAxis) { 90 | /// Rotation about rotAxis with cp & dp 91 | Matrix rotate; 92 | double oneminusc = 1.0 - cosang; 93 | 94 | rotate.e[0] = rotAxis->getx() * rotAxis->getx() * oneminusc + cosang; 95 | rotate.e[1] = rotAxis->getx() * rotAxis->gety() * oneminusc - rotAxis->getz() * sinang; 96 | rotate.e[2] = rotAxis->getx() * rotAxis->getz() * oneminusc + rotAxis->gety() * sinang; 97 | 98 | rotate.e[4] = rotAxis->getx() * rotAxis->gety() * oneminusc + rotAxis->getz() * sinang; 99 | rotate.e[5] = rotAxis->gety() * rotAxis->gety() * oneminusc + cosang; 100 | rotate.e[6] = rotAxis->gety() * rotAxis->getz() * oneminusc - rotAxis->getx() * sinang; 101 | 102 | rotate.e[8] = rotAxis->getx() * rotAxis->getz() * oneminusc - rotAxis->gety() * sinang; 103 | rotate.e[9] = rotAxis->gety() * rotAxis->getz() * oneminusc + rotAxis->getx() * sinang; 104 | rotate.e[10] = rotAxis->getz() * rotAxis->getz() * oneminusc + cosang; 105 | Multiply(rotate); // concatinate rotation with this matrix 106 | m_unit = false; 107 | m_mirrored = -1; // don't know 108 | } 109 | 110 | 111 | void Matrix::Rotate(double angle, int Axis) 112 | { // Rotation (Axis 1 = x , 2 = y , 3 = z 113 | Rotate(sin(angle), cos(angle), Axis); 114 | } 115 | 116 | void Matrix::Rotate(double sinang, double cosang, int Axis) 117 | { // Rotation (Axis 1 = x , 2 = y , 3 = z 118 | Matrix rotate; 119 | rotate.Unit(); 120 | 121 | switch(Axis) 122 | { 123 | case 1: 124 | // about x axis 125 | rotate.e[5] = rotate.e[10] = cosang; 126 | rotate.e[6] = -sinang; 127 | rotate.e[9] = sinang; 128 | break; 129 | case 2: 130 | // about y axis 131 | rotate.e[0] = rotate.e[10] = cosang; 132 | rotate.e[2] = sinang; 133 | rotate.e[8] = -sinang; 134 | break; 135 | case 3: 136 | // about z axis 137 | rotate.e[0] = rotate.e[5] = cosang; 138 | rotate.e[1] = -sinang; 139 | rotate.e[4] = sinang; 140 | break; 141 | } 142 | Multiply(rotate); // concatinate rotation with this matrix 143 | m_unit = false; 144 | m_mirrored = -1; // don't know 145 | } 146 | 147 | void Matrix::Scale(double scale) 148 | { 149 | // add a scale 150 | Scale(scale, scale, scale); 151 | } 152 | 153 | void Matrix::Scale(double scalex, double scaley, double scalez) 154 | { 155 | // add a scale 156 | Matrix temp; 157 | temp.Unit(); 158 | 159 | temp.e[0] = scalex; 160 | temp.e[5] = scaley; 161 | temp.e[10] = scalez; 162 | Multiply(temp); 163 | m_unit = false; 164 | m_mirrored = -1; // don't know 165 | } 166 | void Matrix::Multiply(Matrix& m) 167 | { 168 | // multiply this by give matrix - concatinate 169 | int i, k, l; 170 | Matrix ret; 171 | 172 | for (i = 0; i < 16; i++) 173 | { 174 | l = i - (k = (i % 4)); 175 | ret.e[i] = m.e[l] * e[k] + m.e[l+1] * e[k+4] + m.e[l+2] * e[k+8] + m.e[l+3] * e[k+12]; 176 | } 177 | 178 | *this = ret; 179 | this->IsUnit(); 180 | } 181 | 182 | void Matrix::Transform(double p0[3], double p1[3]) const 183 | { 184 | // transform p0 thro' this matrix 185 | if(m_unit) 186 | memcpy(p1, p0, 3 * sizeof(double)); 187 | else { 188 | p1[0] = p0[0] * e[0] + p0[1] * e[1] + p0[2] * e[2] + e[3]; 189 | p1[1] = p0[0] * e[4] + p0[1] * e[5] + p0[2] * e[6] + e[7]; 190 | p1[2] = p0[0] * e[8] + p0[1] * e[9] + p0[2] * e[10] + e[11]; 191 | } 192 | } 193 | void Matrix::Transform2d(double p0[2], double p1[2]) const 194 | { 195 | // transform p0 thro' this matrix (2d only) 196 | if(m_unit) 197 | memcpy(p1, p0, 2 * sizeof(double)); 198 | else { 199 | p1[0] = p0[0] * e[0] + p0[1] * e[1] + e[3]; 200 | p1[1] = p0[0] * e[4] + p0[1] * e[5] + e[7]; 201 | } 202 | } 203 | 204 | void Matrix::Transform(double p0[3]) const 205 | { 206 | double p1[3]; 207 | if(!m_unit) { 208 | Transform(p0, p1); 209 | memcpy(p0, p1, 3 * sizeof(double)); 210 | } 211 | } 212 | 213 | int Matrix::IsMirrored() 214 | { 215 | // returns true if matrix has a mirror 216 | if(m_unit) 217 | m_mirrored = false; 218 | else if(m_mirrored == -1) { 219 | 220 | m_mirrored = ((e[0] * (e[5] * e[10] - e[6] * e[9]) 221 | - e[1] * (e[4] * e[10] - e[6] * e[8]) 222 | + e[2] * (e[4] * e[9] - e[5] * e[8])) < 0); 223 | } 224 | return m_mirrored; 225 | } 226 | int Matrix::IsUnit() { 227 | // returns true if unit matrix 228 | for(int i = 0; i < 16; i++) { 229 | if(i == 0 || i == 5 || i == 10 || i == 15) { 230 | if(e[i] != 1) return m_unit = false; 231 | } 232 | else { 233 | if(e[i] != 0) return m_unit = false; 234 | } 235 | } 236 | m_mirrored = false; 237 | return m_unit = true; 238 | } 239 | 240 | void Matrix::GetTranslate(double& x, double& y, double& z) const 241 | { 242 | // return translation 243 | x = e[3]; 244 | y = e[7]; 245 | z = e[11]; 246 | } 247 | void Matrix::GetScale(double& sx, double& sy, double& sz) const 248 | { 249 | // return the scale 250 | if(m_unit) { 251 | sx = sy = sz = 1; 252 | } 253 | else { 254 | sx = sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]); 255 | sy = sqrt(e[4] * e[4] + e[5] * e[5] + e[6] * e[6]); 256 | sz = sqrt(e[8] * e[8] + e[9] * e[9] + e[10] * e[10]); 257 | } 258 | } 259 | bool Matrix::GetScale(double& sx) const 260 | { 261 | // return a uniform scale (false if differential) 262 | double sy, sz; 263 | if(m_unit) { 264 | sx = 1; 265 | return true; 266 | } 267 | GetScale(sx, sy, sz); 268 | return (fabs(fabs(sx) - fabs(sy)) < 0.000001)?true : false; 269 | } 270 | void Matrix::GetRotation(double& ax, double& ay, double& az) const 271 | { 272 | // return the rotations 273 | if(m_unit) { 274 | ax = ay = az = 0; 275 | return; 276 | } 277 | double a; /* cos(bx) */ 278 | double b; /* sin(bx) */ 279 | double c; /* cos(by) */ 280 | double d; /* sin(by) */ 281 | double ee; /* cos(bz) */ 282 | double f; /* sin(bz) */ 283 | double sx, sy, sz; 284 | GetScale(sx, sy, sz); 285 | if(this->m_mirrored == -1) FAILURE(L"Don't know mirror - use IsMirrored method on object"); 286 | if(this->m_mirrored) sx = -sx; 287 | 288 | // solve for d and decide case and solve for a, b, c, e and f 289 | d = - e[8] / sz; 290 | if((c = (1 - d) * (1 + d)) > 0.001) 291 | { 292 | // case 1 293 | c = sqrt( c ); 294 | a = e[10] / sz / c; 295 | b = e[9] / sz / c; 296 | ee = e[0] / sx / c; 297 | f = e[4] / sy / c; 298 | } 299 | else 300 | { 301 | // case 2 302 | double coef; 303 | double p, q; 304 | 305 | d = ( d < 0 ) ? -1 : 1 ; 306 | c = 0 ; 307 | p = d * e[5] / sy - e[2] / sx; 308 | q = d * e[6] / sy + e[1] / sx; 309 | if((coef = sqrt( p * p + q * q )) > 0.001) { 310 | a = q / coef; 311 | b = p / coef; 312 | ee = b; 313 | f = -d * b; 314 | } 315 | else 316 | { 317 | /* dependent pairs */ 318 | a = e[5] / sy; 319 | b = -e[6] / sy; 320 | ee = 1 ; 321 | f = 0 ; 322 | } 323 | } 324 | 325 | // solve and return ax, ay and az 326 | ax = atan2( b, a ); 327 | ay = atan2( d, c ); 328 | az = atan2( f, ee ); 329 | } 330 | 331 | Matrix Matrix::Inverse() 332 | { 333 | // matrix inversion routine 334 | 335 | // a is input matrix destroyed & replaced by inverse 336 | // method used is gauss-jordan (ref ibm applications) 337 | 338 | double hold , biga ; 339 | int i , j , k , nk , kk , ij , iz ; 340 | int ki , ji , jp , jk , kj , jq , jr , ik; 341 | 342 | int n = 4; // 4 x 4 matrix only 343 | Matrix a = *this; 344 | int l[4], m[4]; 345 | 346 | if(a.m_unit) return a; // unit matrix 347 | 348 | // search for largest element 349 | nk = - n ; 350 | for ( k = 0 ; k < n ; k++ ) { 351 | nk += n ; 352 | l [ k ] = m [ k ] = k ; 353 | kk = nk + k ; 354 | biga = a.e[ kk ] ; 355 | 356 | for ( j = k ; j < n ; j++ ) { 357 | iz = n * j ; 358 | for ( i = k ; i < n ; i++ ) { 359 | ij = iz + i ; 360 | if ( fabs ( biga ) < fabs ( a.e[ ij ] ) ) { 361 | biga = a.e[ ij ] ; 362 | l[ k ] = i ; 363 | m[ k ] = j ; 364 | } 365 | } 366 | } 367 | 368 | 369 | // interchange rows 370 | j = l[ k ] ; 371 | if ( j > k ) { 372 | ki = k - n ; 373 | 374 | for ( i = 0 ; i < n ; i++ ) { 375 | ki += n ; 376 | hold = - a.e[ ki ] ; 377 | ji = ki - k + j ; 378 | a.e[ ki ] = a.e[ ji ] ; 379 | a.e[ ji ] = hold ; 380 | } 381 | } 382 | 383 | // interchange columns 384 | i = m[ k ] ; 385 | if ( i > k ) { 386 | jp = n * i ; 387 | for ( j = 0 ; j < n ; j++ ) { 388 | jk = nk + j ; 389 | ji = jp + j ; 390 | hold = - a.e[ jk ] ; 391 | a.e[ jk ] = a.e[ ji ] ; 392 | a.e[ ji ] = hold ; 393 | } 394 | } 395 | 396 | // divide columns by minus pivot (value of pivot element is contained in biga) 397 | if ( fabs ( biga ) < 1.0e-10 )FAILURE(getMessage(L"Singular Matrix - Inversion failure",GEOMETRY_ERROR_MESSAGES, -1)); // singular matrix 398 | 399 | for ( i = 0 ; i < n ; i++ ) { 400 | if ( i != k ) { 401 | ik = nk + i ; 402 | a.e[ ik ] = - a.e[ ik ] /biga ; 403 | } 404 | } 405 | 406 | // reduce matrix 407 | for ( i = 0 ; i < n ; i++ ) { 408 | ik = nk + i ; 409 | hold = a.e[ ik ] ; 410 | ij = i - n ; 411 | 412 | for ( j = 0 ; j < n ; j++ ) { 413 | ij = ij + n ; 414 | if ( i != k && j != k ) { 415 | kj = ij - i + k ; 416 | a.e[ ij ] = hold * a.e[ kj ] + a.e[ ij ] ; 417 | } 418 | } 419 | } 420 | 421 | // divide row by pivot 422 | kj = k - n ; 423 | for ( j = 0 ; j < n ; j++ ) { 424 | kj = kj + n ; 425 | if ( j != k ) a.e[ kj] = a.e[ kj ] /biga ; 426 | } 427 | 428 | // replace pivot by reciprocal 429 | a.e[ kk ] = 1 / biga ; 430 | } 431 | 432 | // final row and column interchange 433 | k = n - 1 ; 434 | 435 | while ( k > 0 ) { 436 | i = l[ --k ] ; 437 | if ( i > k ) { 438 | jq = n * k ; 439 | jr = n * i ; 440 | 441 | for ( j = 0 ; j < n ; j++ ) { 442 | jk = jq + j ; 443 | hold = a.e[jk] ; 444 | ji = jr + j ; 445 | a.e[jk] = - a.e[ji] ; 446 | a.e[ji] = hold ; 447 | } 448 | } 449 | 450 | j = m[ k ] ; 451 | if ( j > k ) { 452 | ki = k - n ; 453 | 454 | for ( i = 1 ; i <= n ; i ++ ) { 455 | ki = ki + n ; 456 | hold = a.e[ ki ] ; 457 | ji = ki - k + j ; 458 | a.e[ ki ] = - a.e[ ji ] ; 459 | a.e[ ji ] = hold ; 460 | } 461 | } 462 | } 463 | 464 | return a; 465 | } 466 | 467 | #ifdef PEPSDLL 468 | void Matrix::ToPeps(int id) 469 | { 470 | int set = PepsVdmMake(id, VDM_MATRIX_TYPE , VDM_LOCAL); 471 | if(set < 0) FAILURE(L"Failed to create Matrix VDM"); 472 | struct kgm_header pepsm; 473 | 474 | Get(pepsm.matrix); 475 | pepsm.off_rad = 0; 476 | pepsm.off_dir = pepsm.origin_id = 0; 477 | 478 | PepsVdmWriteTmx(set , &pepsm ); 479 | 480 | PepsVdmClose(set); 481 | 482 | } 483 | 484 | void Matrix::FromPeps(int id) 485 | { 486 | // if(id) { 487 | int set = PepsVdmOpen(id, VDM_MATRIX_TYPE , VDM_READ_ONLY | VDM_LOCAL); 488 | if(set < 0) FAILURE(L"Failed to open Matrix VDM"); 489 | 490 | struct kgm_header pepsm; 491 | PepsVdmReadTmx(set , &pepsm); 492 | memcpy(e, pepsm.matrix, sizeof(pepsm.matrix)); 493 | m_unit = true; 494 | for(int i = 0; i < 16; i++) { 495 | // copy over matrix and check for unit matrix 496 | if(i == 0 || i == 5 || i == 10 || i == 15) { 497 | if((e[i] = pepsm.matrix[i]) != 1) m_unit = false; 498 | } 499 | else { 500 | if((e[i] = pepsm.matrix[i]) != 0) m_unit = false; 501 | } 502 | } 503 | PepsVdmClose(set); 504 | m_mirrored = IsMirrored(); 505 | // } 506 | } 507 | #endif 508 | 509 | Matrix UnitMatrix; // a global unit matrix 510 | 511 | 512 | // vector 513 | Vector2d::Vector2d(const Vector3d &v){ 514 | if(FEQZ(v.getz())) FAILURE(L"Converting Vector3d to Vector2d illegal"); 515 | dx = v.getx(); 516 | dy = v.gety(); 517 | } 518 | 519 | bool Vector2d::operator==(const Vector2d &v)const { 520 | return FEQ(dx, v.getx(), 1.0e-06) && FEQ(dy, v.gety(), 1.0e-06); 521 | } 522 | 523 | void Vector2d::Transform(const Matrix& m) { 524 | // transform vector 525 | if(m.m_unit == false) { 526 | double dxt = dx * m.e[0] + dy * m.e[1]; 527 | double dyt = dx * m.e[4] + dy * m.e[5]; 528 | dx = dxt; 529 | dy = dyt; 530 | } 531 | this->normalise(); 532 | } 533 | 534 | void Vector3d::Transform(const Matrix& m) { 535 | // transform vector 536 | if(m.m_unit == false) { 537 | double dxt = dx * m.e[0] + dy * m.e[1] + dz * m.e[2]; 538 | double dyt = dx * m.e[4] + dy * m.e[5] + dz * m.e[6]; 539 | double dzt = dx * m.e[8] + dy * m.e[9] + dz * m.e[10]; 540 | dx = dxt; 541 | dy = dyt; 542 | dz = dzt; 543 | } 544 | this->normalise(); 545 | } 546 | 547 | void Vector3d::arbitrary_axes(Vector3d& x, Vector3d& y){ 548 | // arbitrary axis algorithm - acad method of generating an arbitrary but 549 | // consistant set of axes from a single normal ( z ) 550 | // arbitrary x & y axes 551 | 552 | if ( ( fabs ( this->getx() ) < 1.0/64.0 ) && (fabs(this->gety()) < 1.0/64.0)) 553 | x = Y_VECTOR ^ *this; 554 | else 555 | x = Z_VECTOR ^ *this; 556 | 557 | y = *this ^ x; 558 | } 559 | 560 | int Vector3d::setCartesianAxes(Vector3d& b, Vector3d& c) { 561 | #define a *this 562 | // computes a RH triad of Axes (Cartesian) starting from a (normalised) 563 | // if a & b are perpendicular then c = a ^ b 564 | // if a & c are perpendicular then b = c ^ a 565 | // if neither are perpendicular to a, then return arbitrary axes from a 566 | 567 | // calling sequence for RH cartesian 568 | // x y z 569 | // y z x 570 | // z x y 571 | if(a == NULL_VECTOR) FAILURE(L"SetAxes given a NULL Vector"); 572 | double epsilon = 1.0e-09; 573 | bool bNull = (b == NULL_VECTOR); 574 | bool cNull = (c == NULL_VECTOR); 575 | bool abPerp = !bNull; 576 | if(abPerp) abPerp = (fabs(a * b) < epsilon); 577 | 578 | bool acPerp = !cNull; 579 | if(acPerp) acPerp = (fabs(a * c) < epsilon); 580 | 581 | if(abPerp) { 582 | c = a ^ b; 583 | return 1; 584 | } 585 | 586 | if(acPerp) { 587 | b = c ^ a; 588 | return 1; 589 | } 590 | 591 | arbitrary_axes(b, c); 592 | b.normalise(); 593 | c.normalise(); 594 | return 2; 595 | } 596 | 597 | 598 | void Plane::Mirrored(Matrix* tmMirrored) { 599 | // calculates a mirror transformation that mirrors 2d about plane 600 | 601 | Point3d p1 = this->Near(Point3d(0.,0.,0.)); 602 | if(tmMirrored->m_unit == false) tmMirrored->Unit(); 603 | 604 | double nx = this->normal.getx(); 605 | double ny = this->normal.gety(); 606 | double nz = this->normal.getz(); 607 | 608 | // the translation 609 | tmMirrored->e[ 3] = -2. * nx * this->d; 610 | tmMirrored->e[ 7] = -2. * ny * this->d; 611 | tmMirrored->e[11] = -2. * nz * this->d; 612 | 613 | // the rest 614 | tmMirrored->e[ 0] = 1. - 2. * nx * nx; 615 | tmMirrored->e[ 5] = 1. - 2. * ny * ny; 616 | tmMirrored->e[10] = 1. - 2. * nz * nz; 617 | tmMirrored->e[ 1] = tmMirrored->e[ 4] = -2. * nx * ny; 618 | tmMirrored->e[ 2] = tmMirrored->e[ 8] = -2. * nz * nx; 619 | tmMirrored->e[ 6] = tmMirrored->e[ 9] = -2. * ny * nz; 620 | 621 | tmMirrored->m_unit = false; 622 | tmMirrored->m_mirrored = true; 623 | } 624 | } 625 | -------------------------------------------------------------------------------- /kurve/offset.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////// 2 | // 2d geometry classes - implements 2d kurve offset for use in dll 3 | // 4 | // g.j.hawkesford August 2003 5 | // 6 | // This program is released under the BSD license. See the file COPYING for details. 7 | // 8 | //////////////////////////////////////////////////////////////////////////////////////////////// 9 | #include "geometry.h" 10 | using namespace geoff_geometry; 11 | 12 | namespace geoff_geometry { 13 | static Kurve eliminateLoops(const Kurve& k , const Kurve& originalk, double offset, int& ret); 14 | static bool DoesIntersInterfere(const Point& pInt, const Kurve& k, double offset); 15 | 16 | int Kurve::Offset(vector&OffsetKurves, double offset, int direction, int method, int& ret)const { 17 | 18 | switch(method) { 19 | case NO_ELIMINATION: 20 | case BASIC_OFFSET: 21 | { 22 | Kurve* ko = new Kurve; 23 | int n = OffsetMethod1(*ko, offset, direction, method, ret); 24 | OffsetKurves.push_back(ko); 25 | return n; 26 | } 27 | 28 | default: 29 | FAILURE(L"Requested Offsetting Method not available"); 30 | } 31 | return 0; 32 | } 33 | 34 | int Kurve::OffsetMethod1(Kurve& kOffset, double off, int direction, int method, int& ret)const 35 | { 36 | // offset kurve with simple span elimination 37 | // direction 1 = left, -1 = right 38 | 39 | // ret = 0 - kurve offset ok 40 | // = 1 - kurve has differential scale (not allowed) 41 | // = 2 - offset failed 42 | // = 3 - offset too large 43 | if(this == &kOffset) FAILURE(L"Illegal Call - 'this' must not be kOffset"); 44 | double offset = (direction == GEOFF_LEFT)?off : -off; 45 | 46 | if(fabs(offset) < geoff_geometry::TOLERANCE || m_nVertices < 2) { 47 | kOffset = *this; 48 | ret = 0; 49 | return 1; 50 | } 51 | 52 | Span curSpan, curSpanOff; // current & offset spans 53 | Span prevSpanOff; // previous offset span 54 | Point p0, p1; // Offset span intersections 55 | 56 | // offset Kurve 57 | kOffset = Matrix(*this); 58 | 59 | if(m_mirrored) offset = -offset; 60 | int RollDir = ( off < 0 ) ? direction : - direction; // Roll arc direction 61 | 62 | double scalex; 63 | if(!GetScale(scalex)) { 64 | ret = 1; 65 | return 0; // differential scale 66 | } 67 | offset /= scalex; 68 | 69 | bool bClosed = Closed(); 70 | int nspans = nSpans(); 71 | if(bClosed) { 72 | Get(nspans, curSpan, true); // assign previus span for closed 73 | 74 | prevSpanOff = curSpan.Offset(offset); 75 | nspans++; // read first again 76 | } 77 | 78 | for(int spannumber = 1; spannumber <= nspans; spannumber++) { 79 | if(spannumber > nSpans()) 80 | Get(1, curSpan, true); // closed kurve - read first span again 81 | else 82 | Get(spannumber, curSpan, true); 83 | 84 | if(!curSpan.NullSpan) { 85 | int numint = 0; 86 | curSpanOff = curSpan.Offset(offset); 87 | curSpanOff.ID = 0; 88 | if(!kOffset.m_started) { 89 | kOffset.Start(curSpanOff.p0); 90 | kOffset.AddSpanID(0); 91 | } 92 | 93 | if(spannumber > 1) { 94 | // see if tangent 95 | double d = curSpanOff.p0.Dist(prevSpanOff.p1); 96 | if((d > geoff_geometry::TOLERANCE) && (curSpanOff.NullSpan == false && prevSpanOff.NullSpan == false)) { 97 | // see if offset spans intersect 98 | 99 | double cp = prevSpanOff.ve ^ curSpanOff.vs; 100 | bool inters = (cp > 0 && direction == GEOFF_LEFT) || (cp < 0 && direction == GEOFF_RIGHT); 101 | 102 | if(inters) { 103 | double t[4]; 104 | numint = prevSpanOff.Intof(curSpanOff, p0, p1, t); 105 | } 106 | 107 | if(numint == 1) { 108 | // intersection - modify previous endpoint 109 | kOffset.Replace(kOffset.m_nVertices-1, prevSpanOff.dir, p0, prevSpanOff.pc, prevSpanOff.ID); 110 | } 111 | else { 112 | // 0 or 2 intersections, add roll around (remove -ve loops in elimination function) 113 | if(kOffset.Add(RollDir, curSpanOff.p0, curSpan.p0, false)) kOffset.AddSpanID(ROLL_AROUND); 114 | } 115 | } 116 | } 117 | 118 | // add span 119 | if(spannumber < m_nVertices) { 120 | curSpanOff.ID = spannumber; 121 | kOffset.Add(curSpanOff, false); 122 | } 123 | else if(numint == 1) // or replace the closed first span 124 | kOffset.Replace(0, 0, p0, Point(0, 0), 0); 125 | 126 | } 127 | if(!curSpanOff.NullSpan)prevSpanOff = curSpanOff; 128 | } // end of main pre-offsetting loop 129 | 130 | 131 | #ifdef _DEBUG 132 | //testDraw->AddKurve("", &kOffset, 0, GREEN); 133 | // outXML oxml(L"c:\\temp\\eliminateLoops.xml"); 134 | // oxml.startElement(L"eliminateLoops"); 135 | // oxml.Write(kOffset, L"kOffset"); 136 | // oxml.endElement(); 137 | #endif 138 | // eliminate loops 139 | if(method == NO_ELIMINATION) { 140 | ret = 0; 141 | return 1; 142 | } 143 | kOffset = eliminateLoops(kOffset, *this, offset, ret); 144 | 145 | if(ret == 0 && bClosed) { 146 | // check for inverted offsets of closed kurves 147 | if(kOffset.Closed()) { 148 | double a = Area(); 149 | int dir = (a < 0); 150 | double ao = kOffset.Area(); 151 | int dirOffset = ao < 0; 152 | 153 | if(dir != dirOffset) 154 | ret = 3; 155 | else { 156 | // check area change compatible with offset direction - catastrophic failure 157 | bool bigger = (a > 0 && offset > 0) || (a < 0 && offset < 0); 158 | if(bigger && fabs(ao) < fabs(a)) ret = 2; 159 | } 160 | } 161 | else 162 | ret = 2; // started closed but now open?? 163 | } 164 | return (ret == 0)?1 : 0; 165 | } 166 | 167 | 168 | static Kurve eliminateLoops(const Kurve& k , const Kurve& originalk, double offset, int& ret) { 169 | // a simple loop elimination routine based on first offset ideas in Peps 170 | // this needs extensive work for future 171 | // start point musn't disappear & only one valid offset is determined 172 | // 173 | // ret = 0 for ok 174 | // ret = 2 for impossible geometry 175 | 176 | Span sp0, sp1; 177 | Point pInt, pIntOther; 178 | 179 | Kurve ko; // eliminated output 180 | ko = Matrix(k); 181 | int kinVertex = 0; 182 | 183 | while(kinVertex <= k.nSpans()) { 184 | bool clipped = false ; // not in a clipped section (assumption with this simple method) 185 | 186 | sp0.dir = k.Get(kinVertex, sp0.p0, sp0.pc); 187 | sp0.ID = k.GetSpanID(kinVertex++); 188 | if (kinVertex == 1) { 189 | ko.Start(sp0.p0); // start point mustn't dissappear for this simple method 190 | ko.AddSpanID(sp0.ID); 191 | } 192 | if (kinVertex <= k.nSpans()) { // any more? 193 | int ksaveVertex = kinVertex ; 194 | sp0.dir = k.Get(kinVertex, sp0.p1, sp0.pc); // first span 195 | sp0.ID = k.GetSpanID(kinVertex++); 196 | 197 | sp0.SetProperties(true); 198 | 199 | int ksaveVertex1 = kinVertex; // mark position AA 200 | if (kinVertex <= k.nSpans()) { // get the next but one span 201 | sp1.dir = k.Get(kinVertex, sp1.p0, sp1.pc); 202 | sp1.ID = k.GetSpanID(kinVertex++); 203 | int ksaveVertex2 = kinVertex; // mark position BB 204 | 205 | int fwdCount = 0; 206 | while(kinVertex <= k.nSpans()) { 207 | sp1.dir = k.Get(kinVertex, sp1.p1, sp1.pc); // check span 208 | sp1.ID = k.GetSpanID(kinVertex++); 209 | sp1.SetProperties(true); 210 | 211 | double t[4]; 212 | int numint = sp0.Intof(sp1, pInt, pIntOther, t); // find span intersections 213 | if(numint && sp0.p0.Dist(pInt) < geoff_geometry::TOLERANCE ) numint=0; // check that intersection is not at the start of the check span 214 | if(numint ) { 215 | 216 | if(numint == 2) { 217 | // choose first intercept on sp0 218 | Span spd = sp0; 219 | spd.p1 = pInt; 220 | spd.SetProperties(true); 221 | double dd = spd.length; 222 | 223 | spd.p1 = pIntOther; 224 | spd.SetProperties(true); 225 | if(dd > spd.length) pInt = pIntOther; 226 | numint = 1; 227 | 228 | } 229 | ksaveVertex = ksaveVertex1 ; 230 | 231 | clipped = true ; // in a clipped section 232 | if(DoesIntersInterfere(pInt, originalk, offset) == false) { 233 | sp0.p1 = pInt; // ok so truncate this span to the intersection 234 | clipped = false; // end of clipped section 235 | break; 236 | } 237 | // no valid intersection found so carry on 238 | } 239 | sp1.p0 = sp1.p1 ; // next 240 | ksaveVertex1 = ksaveVertex2 ; // pos AA = BB 241 | ksaveVertex2 = kinVertex; // mark 242 | 243 | if((kinVertex > k.nSpans() || fwdCount++ > 25) && clipped == false) break; 244 | } 245 | } 246 | 247 | if(clipped) { 248 | ret = 2; // still in a clipped section - error 249 | 250 | return ko; 251 | } 252 | 253 | ko.Add(sp0, false); 254 | 255 | kinVertex = ksaveVertex; 256 | } 257 | } 258 | ret = 0; 259 | 260 | return ko; // no more spans - seems ok 261 | } 262 | 263 | 264 | static bool DoesIntersInterfere(const Point& pInt, const Kurve& k, double offset) { 265 | // check that intersections don't interfere with the original kurve 266 | Span sp; 267 | Point dummy; 268 | int kCheckVertex = 0; 269 | k.Get(kCheckVertex++, sp.p0, sp.pc); 270 | 271 | offset = fabs(offset) - geoff_geometry::TOLERANCE; 272 | while(kCheckVertex <= k.nSpans()) { 273 | sp.dir = k.Get(kCheckVertex++, sp.p1, sp.pc); 274 | sp.SetProperties(true); 275 | // check for interference 276 | if(Dist(sp, pInt, dummy) < offset) return true; 277 | sp.p0 = sp.p1; 278 | } 279 | return false; // intersection is ok 280 | } 281 | } 282 | 283 | 284 | static struct iso { 285 | Span sp; 286 | Span off; 287 | } isodata; 288 | static void isoRadius(Span& before, Span& blend, Span& after, double radius); 289 | 290 | int Kurve::OffsetISOMethod(Kurve& kOut, double off, int direction, bool BlendAll)const { 291 | // produces a special offset Kurve - observing so-called ISO radii 292 | // eg line/arc/line tangent - keep arc radius constant 293 | // this method also considers arc/arc/arc etc. 294 | // interior radius must be smallest of triplet for above. 295 | 296 | // parameters:- 297 | // Output kOut resulting kurve 298 | // Input off offset amount 299 | // Input direction offset direction (LEFT or RIGHT) 300 | // Input BlendAall if false only consider ISO radius for LINE/ARC/LINE 301 | // if true consider all blended radii (ARC/ARC/ARC etc.) 302 | double offset = (direction == GEOFF_LEFT)?off : -off; 303 | if(FEQZ(off) || nSpans() < 1) { 304 | kOut = *this; 305 | return 1; 306 | } 307 | double cptol = 1.0e-05; 308 | std::vector spans; 309 | for(int i = 0; i < nSpans(); i++) { // store all spans and offsets 310 | Get(i+1, isodata.sp, true, true); 311 | isodata.off = isodata.sp.Offset(offset); 312 | spans.push_back(isodata); 313 | } 314 | 315 | for(int i = 0; i < nSpans() - 1; i++) // calculate intersections for none tangent spans 316 | if(fabs(spans[i].off.ve ^ spans[i+1].off.vs) > cptol) spans[i].off.JoinSeparateSpans(spans[i+1].off); 317 | 318 | for(int i = 1; i < nSpans() - 1; i++) { // deal with isoradii 319 | if(spans[i].off.dir) { 320 | if(BlendAll) { // interior radius should be smaller than neighbours 321 | if(spans[i-1].sp.dir) 322 | if(spans[i-1].sp.radius < spans[i].sp.radius) continue; 323 | if(spans[i+1].sp.dir) 324 | if(spans[i+1].sp.radius < spans[i].sp.radius) continue; 325 | } 326 | else { 327 | if((spans[i-1].off.dir || spans[i+1].off.dir)) continue; // linear neighbours only 328 | } 329 | 330 | if((fabs(spans[i-1].sp.ve ^ spans[i].sp.vs) < cptol) && (fabs(spans[i].sp.ve ^ spans[i+1].sp.vs) < cptol)) { 331 | // isoradius - calculate the new offset radius and modify neighbouring spans 332 | isoRadius(spans[i-1].off, spans[i].off, spans[i+1].off, spans[i].sp.radius); 333 | } 334 | } 335 | } 336 | 337 | kOut.Start(spans[0].off.p0); // start point 338 | for(int i = 0; i < nSpans(); i++) 339 | kOut.Add(spans[i].off.dir, spans[i].off.p1, spans[i].off.pc); // output all spans 340 | return 1; 341 | } 342 | 343 | static void isoRadius(Span& before, Span& blend, Span& after, double radius) { 344 | // calculate the new offset radius and modify neighbouring spans 345 | int direction = ((before.ve ^ after.vs) > 0)? 1 : -1; // offset direction 346 | Span beforeOff = before.Offset(direction * radius); 347 | Span afterOff = after.Offset(direction * radius); 348 | int turnLeft = ((before.ve ^ after.vs) > 0)? 1 : -1; 349 | if(before.dir == LINEAR) { 350 | CLine b(beforeOff); 351 | if(after.dir == LINEAR) { 352 | CLine a(afterOff); 353 | blend.pc = b.Intof(a); 354 | } 355 | else { 356 | Circle a(afterOff); 357 | b.Intof(turnLeft * after.dir, a, blend.pc); 358 | } 359 | } 360 | else { 361 | Circle b(beforeOff); 362 | 363 | if(after.dir == LINEAR) { 364 | CLine a(afterOff); 365 | a.Intof(-turnLeft * before.dir, b, blend.pc); 366 | } 367 | else { 368 | // arc arc 369 | Circle a(afterOff); 370 | int leftright = ((Vector2d(b.pc, blend.pc) ^ Vector2d(b.pc, a.pc)) < 0)? 1 : -1; 371 | b.Intof(leftright, a, blend.pc); 372 | } 373 | } 374 | before.p1 = blend.p0 = before.Near(blend.pc); 375 | after.p0 = blend.p1 = after.Near(blend.pc); 376 | } 377 | -------------------------------------------------------------------------------- /test.bat: -------------------------------------------------------------------------------- 1 | c:\python26\python.exe test.py 2 | pause -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import area 2 | 3 | p = area.Point(0,0) 4 | 5 | m = area.Matrix([1,0,0,12, 0,1,0,0, 0,0,1,0, 0,0,0,1]) 6 | 7 | p.Transform(m) 8 | 9 | print p.x, p.y --------------------------------------------------------------------------------