angle-between-vectors does not handle tolerance inputs correctly
Created by: reiniervandijk
Function angle-between-vectors
has a keyword argument (epsilon *zero-epsilon*)
to give the user control over how small of an angle is considered to be zero. However, the implementation of the code is such that epsilon
has effectively not effect. Replicate using:
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.05 0) (make-vector 0 0 1))
0.049958395721943306
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.04 0) (make-vector 0 0 1))
0.0
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.04 0) (make-vector 0 0 1) :epsilon 1e-9)
0.0
gdl-user>
Imported comments:
By reiniervandijk on 2015-01-30 12:44:31 UTC
The fix to this issue is:
(in-package :geom-base)
(defun unitize-vector (vector &key (epsilon *zero-epsilon*)) ;; FIX: extra epsilon
"Unit Vector. Returns the normalized unit-length vector corresponding to <b>vector</b>.
:arguments (vector \"3D Vector\")"
(when (and *zero-vector-checking?* (zero-vector? vector))
(error "~s has Euclidean length zero; cannot unitize." vector))
(if (near-to? (length-vector vector) 1.0 epsilon) ;; FIX: extra epsilon
vector
(scalar*vector (/ (length-vector vector)) vector)))
(defun angle-between-vectors (vector-1 vector-2 &optional reference-vector &key (epsilon *zero-epsilon*) -ve)
"Number. Returns the angle in radians between <b>vector-1</b> and <b>vector-2</b>.
If no <b>reference-vector</b> given, the smallest possible angle is returned.
If a <b>reference-vector</b> is given, computes according to the right-hand rule.
If <b>-ve</b> is given, returns a negative number for angle if it really is
negative according to the right-hand rule.
:arguments (vector-1 \"3D Vector\"
vector-2 \"3D Vector\")
:&optional ((reference-vector nil) \"3D Vector\")
:&key ((epsilon *zero-epsilon*) \"Number. Determines how small of an angle is considered to be zero.\"
(-ve nil) \"Boolean\")"
(let ((vector-1 (unitize-vector vector-1 :epsilon epsilon))
(vector-2 (unitize-vector vector-2 :epsilon epsilon)))
(let ((dot-vector (dot-vectors vector-1 vector-2)))
(when (> dot-vector 1.0d0) (setq dot-vector 1.0d0))
(when (< dot-vector -1.0d0) (setq dot-vector -1.0d0))
(let ((smallest-angle (acos dot-vector)))
(when (< smallest-angle epsilon) (setq smallest-angle 0.0d0))
(let ((try (if (and reference-vector
(minusp (dot-vectors reference-vector (cross-vectors vector-1 vector-2))))
(- (twice pi) smallest-angle)
smallest-angle)))
(if (and -ve (> try pi)) (- try (twice pi)) try))))))
Now try:
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.05 0) (make-vector 0 0 1))
0.049958395721943306
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.04 0) (make-vector 0 0 1))
0.0
gdl-user> (angle-between-vectors (make-vector 1 0 0) (make-vector 1 0.04 0) (make-vector 0 0 1) :epsilon 1e-9)
0.03997868712329394
By genworks on 2015-03-11 23:41:29 UTC
Merged and committed into devo branch.