Solve 102 with barycentric coordinates instead
author |
Steve Losh <steve@stevelosh.com> |
date |
Fri, 22 Sep 2017 19:33:49 -0400 |
parents |
02ac02c3f88d
|
children |
c81a4bca6e78
|
branches/tags |
(none) |
files |
src/problems.lisp src/utils.lisp |
Changes
--- a/src/problems.lisp Fri Sep 22 19:11:14 2017 -0400
+++ b/src/problems.lisp Fri Sep 22 19:33:49 2017 -0400
@@ -1726,49 +1726,17 @@
(iterate (for line :in-file file :using #'read-line)
(for (ax ay bx by cx cy) =
(mapcar #'parse-integer (cl-strings:split line #\,)))
- (collect (list (cons ax ay)
- (cons bx by)
- (cons cx cy)))))
- (find-side-vertical (a b p)
- "Return which side of the line AB point P falls on."
- (destructuring-bind (ax . ay) a
- (destructuring-bind (bx . by) b
- (if (= ax bx)
- (find-side-horizontal a b p)
- (destructuring-bind (px . py) p
- (let* ((slope (/ (- ay by)
- (- ax bx)))
- (y-intercept (- ay (* slope ax)))
- (line-y (+ y-intercept (* slope px))))
- (cond
- ((> py line-y) :above)
- ((< py line-y) :below)
- ((= py line-y) :on))))))))
- (find-side-horizontal (a b p)
- "Return which side of the line AB point P falls on."
- (destructuring-bind (ax . ay) a
- (destructuring-bind (bx . by) b
- (if (= ay by)
- (find-side-vertical a b p)
- (destructuring-bind (px . py) p
- (let* ((slope (/ (- ax bx)
- (- ay by)))
- (x-intercept (- ax (* slope ay)))
- (line-x (+ x-intercept (* slope py))))
- (cond
- ((> px line-x) :right)
- ((< px line-x) :left)
- ((= px line-x) :on))))))))
- (check-line (a b c p)
- (let ((result-triangle (find-side-vertical a b c))
- (result-point (find-side-vertical a b p)))
- (or (eq result-point :on)
- (eq result-point result-triangle))))
+ (collect (list (vec2 ax ay)
+ (vec2 bx by)
+ (vec2 cx cy)))))
(check-triangle (a b c)
- (let ((origin (cons 0 0)))
- (and (check-line a b c origin)
- (check-line c a b origin)
- (check-line b c a origin)))))
+ ;; A point is within a triangle if its barycentric coordinates
+ ;; (with respect to that triangle) are all within 0 to 1.
+ (multiple-value-bind (u v w)
+ (barycentric a b c (vec2 0 0))
+ (and (<= 0 u 1)
+ (<= 0 v 1)
+ (<= 0 w 1)))))
(iterate (for (a b c) :in (parse-file "data/102-triangles.txt"))
(counting (check-triangle a b c)))))
--- a/src/utils.lisp Fri Sep 22 19:11:14 2017 -0400
+++ b/src/utils.lisp Fri Sep 22 19:33:49 2017 -0400
@@ -535,3 +535,50 @@
(coerce (let ((div (expt 10 precision)))
(/ (funcall rounder (* number div)) div))
(type-of number)))
+
+
+(defun vec2 (x y)
+ (vector x y))
+
+(defun vx (vec2)
+ (aref vec2 0))
+
+(defun vy (vec2)
+ (aref vec2 1))
+
+(defun vec2+ (a b)
+ (vec2 (+ (vx a) (vx b))
+ (+ (vy a) (vy b))))
+
+(defun vec2- (a b)
+ (vec2 (- (vx a) (vx b))
+ (- (vy a) (vy b))))
+
+(defun vec2-dot (a b)
+ (+ (* (vx a) (vx b))
+ (* (vy a) (vy b))))
+
+
+(defun barycentric (a b c point)
+ "Return the barycentric coords of `point` with respect to the triangle `abc`.
+
+ `a`, `b`, `c`, and `point` must be `vec2`s.
+
+ The resulting `u`, `v`, and `w` barycentric coordinates will be returned as
+ three values.
+
+ "
+ ;; https://gamedev.stackexchange.com/a/23745
+ (let* ((v0 (vec2- b a))
+ (v1 (vec2- c a))
+ (v2 (vec2- point a))
+ (d00 (vec2-dot v0 v0))
+ (d01 (vec2-dot v0 v1))
+ (d11 (vec2-dot v1 v1))
+ (d20 (vec2-dot v2 v0))
+ (d21 (vec2-dot v2 v1))
+ (denom (- (* d00 d11) (* d01 d01)))
+ (v (/ (- (* d11 d20) (* d01 d21)) denom))
+ (w (/ (- (* d00 d21) (* d01 d20)) denom))
+ (u (- 1 v w)))
+ (values u v w)))