-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathpolygon-binary.lisp
90 lines (81 loc) · 4.73 KB
/
polygon-binary.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
(in-package :2d-geometry)
;;;; This file implements union, intersection and difference of polygons using triangulation of edge sets.
(defun merge-line-segment-into (ls1 ls2)
"If two segments are colinear and intersect, extends the first one to include the second. Reorients the first edge to the left."
(if (line-segments-intersection-segment ls1 ls2)
(let ((left-ls1 (left-endpoint ls1))
(left-ls2 (left-endpoint ls2))
(right-ls1 (right-endpoint ls1))
(right-ls2 (right-endpoint ls2)))
(setf (start ls1) (if (point-sort-fun left-ls1 left-ls2)
left-ls1
left-ls2)
(end ls1) (if (point-sort-fun right-ls1 right-ls2)
right-ls2
right-ls1))
t)
nil))
(defun recurse-sanitize-edges (edge-list acc)
(if (null edge-list)
(nreverse acc)
(let ((head (car edge-list))
(rst (cdr edge-list))
(racc nil))
(if (point-equal-p (start head) (end head))
(sanitize-edges rst acc)
(progn
(dolist (tk rst)
(unless (merge-line-segment-into head tk)
(push tk racc)))
(sanitize-edges racc (cons head acc)))))))
(defun sanitize-edges (edge-list)
"Drop zero length edges and merge all segment intersecting edges."
(recurse-sanitize-edges (mapcar #'copy-line-segment edge-list) nil))
(defun polygon-binary (polygon1 polygon2 triangle-test)
"Return all triangles fulfilling triangle-test from triangulation of all edges of two polygons."
(let ((edge-list (sanitize-edges (append (edge-list polygon1)
(edge-list polygon2)))))
(let ((trapez (trapezoidize-edges edge-list)))
(let ((triangles (trapezoids-to-triangles trapez)))
(remove-if-not triangle-test triangles)))))
(defun polygon-union (polygon1 polygon2 &key (in-test 'point-in-polygon-winding-p) (in-test-1 nil) (in-test-2 nil))
"Return triangles of an union of two polygons."
(let ((in-1 (if in-test-1 in-test-1 in-test))
(in-2 (if in-test-2 in-test-2 in-test)))
(polygon-binary polygon1 polygon2 #'(lambda (x)
(or (funcall in-1 (triangle-center-point x) polygon1)
(funcall in-2 (triangle-center-point x) polygon2))))))
(defun polygon-intersection (polygon1 polygon2 &key (in-test 'point-in-polygon-winding-p) (in-test-1 nil) (in-test-2 nil))
"Return triangles of an intersection of two polygons."
(let ((in-1 (if in-test-1 in-test-1 in-test))
(in-2 (if in-test-2 in-test-2 in-test)))
(polygon-binary polygon1 polygon2 #'(lambda (x)
(and (funcall in-1 (triangle-center-point x) polygon1)
(funcall in-2 (triangle-center-point x) polygon2))))))
(defun polygon-difference (polygon1 polygon2 &key (in-test 'point-in-polygon-winding-p) (in-test-1 nil) (in-test-2 nil))
"Return triangles of polygon1 minus polygon2."
(let ((in-1 (if in-test-1 in-test-1 in-test))
(in-2 (if in-test-2 in-test-2 in-test)))
(polygon-binary polygon1 polygon2 #'(lambda (x)
(and (funcall in-1 (triangle-center-point x) polygon1)
(not (funcall in-2 (triangle-center-point x) polygon2)))))))
(defun polygon-difference-nary (polygon &rest holes &key (in-test 'point-in-polygon-winding-p))
"Return triangles of polygon with some holes."
(let ((edge-list (sanitize-edges (append (edge-list polygon)
(reduce #'append
(mapcar #'edge-list
(remove-if-not
#'(lambda (poly)
(typecase poly
(polygon t)
(t nil)))
holes)))))))
(let ((trapez (trapezoidize-edges edge-list)))
(let ((triangles (trapezoids-to-triangles trapez)))
(remove-if-not #'(lambda (x)
(let ((center-point (triangle-center-point x)))
(and (funcall in-test center-point polygon)
(every #'(lambda (hole)
(not (funcall in-test center-point hole)))
holes))))
triangles)))))