/** * Returns the Chessboard distance to another vector * Definition: chessboard dist. = max(|x1 - x2|, |y1 - y2|, ...) * * @param Math_Vector $vector Math_Vector object * * @return float on success * @throws InvalidArgumentException */ public function chessboardDistance(Math_Vector $vector) { if (!Math_VectorOp::isVector($vector)) { throw new InvalidArgumentException("Wrong parameter type, expecting a Math_Vector object"); } $n = $this->size(); if ($vector->size() != $n) { throw new InvalidArgumentException("Vector has to be of the same size"); } $sum = 0; $cdist = array(); for ($i = 0; $i < $n; $i++) { $cdist[] = abs($this->_tuple->getElement($i) - $vector->_tuple->getElement($i)); } return max($cdist); }
/** * Calculates the intersection point bnetween the current and two other planes * * @param Plane $plane1 * @param Plane $plane2 * @return bool|\Math_Vector3 * @throws InvalidArgumentException */ public function calculateIntersectionPointWithTwoPlanes(self $plane1, self $plane2) { $n1 = $this->normalVectorNormalized; $n2 = $plane1->normalVectorNormalized; $n3 = $plane2->normalVectorNormalized; $d1 = $this->distanceToOrigin; $d2 = $plane1->distanceToOrigin; $d3 = $plane2->distanceToOrigin; $n2_x_n3 = \Math_VectorOp::crossProduct($n2, $n3); $n3_x_n1 = \Math_VectorOp::crossProduct($n3, $n1); $n1_x_n2 = \Math_VectorOp::crossProduct($n1, $n2); $p = new \Math_Vector3(\Math_VectorOp::add(\Math_VectorOp::add(\Math_VectorOp::scale($d1, $n2_x_n3), \Math_VectorOp::scale($d2, $n3_x_n1)), \Math_VectorOp::scale($d3, $n1_x_n2))->getTuple()); $divisor = \Math_VectorOp::dotProduct($n1, $n2_x_n3); if ((double) 0 === $divisor) { throw new \InvalidArgumentException('no point-intersection'); } $p->scale(1 / $divisor); return $p; }
/** * Solves a system of linear equations: Ax = b, using an iterative error correction algorithm * * A system such as: * <pre> * a11*x1 + a12*x2 + ... + a1n*xn = b1 * a21*x1 + a22*x2 + ... + a2n*xn = b2 * ... * ak1*x1 + ak2*x2 + ... + akn*xn = bk * </pre> * can be rewritten as: * <pre> * Ax = b * </pre> * where: * - A is matrix of coefficients (aij, i=1..k, j=1..n), * - b a vector of values (bi, i=1..k), * - x the vector of unkowns (xi, i=1..n) * Using: x = (Ainv)*b * where: * - Ainv is the inverse of A * * The error correction algorithm uses the approach that if: * - xp is the approximate solution * - bp the values obtained from pluging xp into the original equation * We obtain * <pre> * A(x - xp) = (b - bp), * or * A*xadj = (b - bp) * </pr> * where: * - xadj is the adjusted value (= Ainv*(b - bp)) * therefore, we calculate iteratively new values of x using the estimated * xadj and testing to check if we have decreased the error. * * @static * @access public * @param object Math_Matrix $a the matrix of coefficients * @param object Math_Vector $b the vector of values * @return mixed a Math_Vector object on succcess, PEAR_Error otherwise * @see vectorMultiply() * @see invert() * @see Math_VectorOp::add() * @see Math_VectorOp::substract() * @see Math_VectorOp::length() */ function solveEC($a, $b) { /*{{{*/ $ainv = $a->clone(); $e = $ainv->invert(); if (PEAR::isError($e)) { return $e; } $x = $ainv->vectorMultiply($b); if (PEAR::isError($x)) { return $x; } // initial guesses $bprime = $a->vectorMultiply($x); if (PEAR::isError($bprime)) { return $bprime; } $err = Math_VectorOp::substract($b, $bprime); $adj = $ainv->vectorMultiply($err); if (PEAR::isError($adj)) { return $adj; } $adjnorm = $adj->length(); $xnew = $x; // compute new solutions and test for accuracy // iterate no more than 10 times for ($i = 0; $i < 10; $i++) { $xnew = Math_VectorOp::add($x, $adj); $bprime = $a->vectorMultiply($xnew); $err = Math_VectorOp::substract($b, $bprime); $newadj = $ainv->vectorMultiply($err); $newadjnorm = $newadj->length(); // did we improve the accuracy? if ($newadjnorm < $adjnorm) { $adjnorm = $newadjnorm; $x = $xnew; $adj = $newadj; } else { // we did improve the accuracy, so break; break; } } return $x; }
/** * Angle between vectors, using the equation: v . w = |v| |w| cos(theta) * * @access public * @param object Math_Vector2 or MathVector3 (or subclass) $v1 * @param object Math_Vector2 or MathVector3 (or subclass) $v2 * @return mixed the angle between vectors (float, in radians) on success, a PEAR_Error object otherwise * * @see isVector2() * @see isVector3() * @see dotProduct() */ function angleBetween($v1, $v2) { if (Math_VectorOp::isVector2($v1) && Math_VectorOp::isVector2($v2) || Math_VectorOp::isVector3($v1) && Math_VectorOp::isVector3($v2)) { $v1->normalize(); $v2->normalize(); return acos(Math_VectorOp::dotProduct($v1, $v2)); } else { return PEAR::raiseError("Vectors must be both of the same type"); } }
/** * Returns the Chessboard distance to another vector * Definition: chessboard dist. = max(|x1 - x2|, |y1 - y2|, ...) * * @access public * @param object $vector Math_Vector object * @return float on success, a PEAR_Error object on failure */ function chessboardDistance($vector) { $n = $this->size(); $sum = 0; if (Math_VectorOp::isVector($vector)) { if ($vector->size() == $n) { $cdist = array(); for ($i = 0; $i < $n; $i++) { $cdist[] = abs($this->tuple->getElement($i) - $vector->tuple->getElement($i)); } return max($cdist); } else { return PEAR::raiseError("Vector has to be of the same size"); } } else { return PEAR::raiseError("Wrong parameter type, expecting a Math_Vector object"); } }
/** * Takes all point vectors ("vertexes") of the polygon describing the face and sorts them * in clockwise order. */ public function sortVerticesClockwise() { $center = $this->calculateCenter(); for ($n = 0; $n <= count($this->vertexes) - 3; $n++) { $a = new \Math_Vector3(\Math_VectorOp::substract($this->vertexes[$n], $center)->getTuple()); $a->normalize(); $p = Plane::getInstanceByThreePositionVectors($this->vertexes[$n], $center, new \Math_Vector3(\Math_VectorOp::add($center, $this->plane->getNormalVectorNormalized())->getTuple())); $smallestAngle = -1; $smallest = -1; for ($m = $n + 1; $m <= count($this->vertexes) - 1; $m++) { if ($p->calculateSideOfPointVector($this->vertexes[$m]) !== Plane::SIDE_BACK) { $b = new \Math_Vector3(\Math_VectorOp::substract($this->vertexes[$m], $center)->getTuple()); $b->normalize(); $angle = \Math_VectorOp::dotProduct($a, $b); if ($angle > $smallestAngle) { $smallestAngle = $angle; $smallest = $m; } } } if ($smallest == -1) { throw new \RuntimeException('Error: Degenerate polygon!'); } //swap vertices $temp = $this->vertexes[$n + 1]; $this->vertexes[$n + 1] = $this->vertexes[$smallest]; $this->vertexes[$smallest] = $temp; unset($temp); } // Check if vertex order needs to be reversed for back-facing polygon $newPlane = Plane::getInstanceByThreePositionVectors($this->vertexes[0], $this->vertexes[1], $this->vertexes[2]); if (\Math_VectorOp::dotProduct($newPlane->getNormalVectorNormalized(), $this->plane->getNormalVectorNormalized()) < 0) { array_reverse($this->vertexes); } }
echo date("Y-m-d H:i:s") . "\n"; echo "==\nVector v1: " . $v1->toString() . "\n"; echo "Vector v2: " . $v2->toString() . "\n"; $r = Math_VectorOp::add($v1, $v2); echo "v1 + v2: " . $r->toString() . "\n"; $r = Math_VectorOp::substract($v1, $v2); echo "v1 - v2: " . $r->toString() . "\n"; $r = Math_VectorOp::multiply($v1, $v2); echo "v1 * v2: " . $r->toString() . "\n"; $r = Math_VectorOp::divide($v1, $v2); echo "v1 / v2: " . $r->toString() . "\n"; echo "==\nVector w1: " . $w1->toString() . "\n"; echo "Vector w2: " . $w2->toString() . "\n"; echo "Vector w3: " . $w3->toString() . "\n"; $r = Math_VectorOp::scale(2.0, $w1); echo " 2.0 * w1 = " . $r->toString() . "\n"; $r = Math_VectorOp::dotProduct($w1, $w2); echo "w1 . w2 = {$r}\n"; $r = Math_VectorOp::crossProduct($w2, $w3); echo "w2 x w3 = " . $r->toString() . "\n"; echo "The triple scalar product of 3 vectors is the volume\r\nof the parallelepiped defined by the vectors. Three coplanar\r\nvectors must give a volume of zero, w1, w2 and w3 are coplanar\n"; $r = Math_VectorOp::tripleScalarProduct($w1, $w2, $w3); echo "w1 . (w2 x w3) = {$r}\n"; $z = Math_VectorOp::createOne(3); echo "Now we introduce z : " . $z->toString() . "\n"; echo "and z not being coplanar to w1, w2, or w3:\n"; $r = Math_VectorOp::tripleScalarProduct($z, $w2, $w3); echo "z * (w2 x w3) = {$r}\n"; $r = Math_VectorOp::angleBetween($z, $w1); echo "and the angle between z and w1 is {$r} radians\n"; echo "which is " . $r * 180.0 / M_PI . " degrees\n";
/** * Verify that an exception is thrown when trying to get the intersection point with two other parallel planes */ public function testIntersectionAllParalel() { $this->setExpectedException('InvalidArgumentException'); $p1 = Plane::getInstanceByThreePositionVectors($this->x1, $this->y1, $this->z1); $add1 = new Math_Vector3(new Math_Tuple(array(0, 0, 16))); $x1p = new Math_Vector3(Math_VectorOp::add($this->x1, $add1)->getTuple()); $y1p = new Math_Vector3(Math_VectorOp::add($this->y1, $add1)->getTuple()); $z1p = new Math_Vector3(Math_VectorOp::add($this->z1, $add1)->getTuple()); $p2 = Plane::getInstanceByThreePositionVectors($x1p, $y1p, $z1p); $add2 = new Math_Vector3(new Math_Tuple(array(0, 0, 32))); $x1p2 = new Math_Vector3(Math_VectorOp::add($this->x1, $add2)->getTuple()); $y1p2 = new Math_Vector3(Math_VectorOp::add($this->y1, $add2)->getTuple()); $z1p2 = new Math_Vector3(Math_VectorOp::add($this->z1, $add2)->getTuple()); $p3 = Plane::getInstanceByThreePositionVectors($x1p2, $y1p2, $z1p2); $p1->calculateIntersectionPointWithTwoPlanes($p2, $p3); }
function testDotProduct() { $this->assertEquals(2, Math_VectorOp::dotProduct($this->x, $this->x)); }
/** * Angle between vectors, using the equation: v . w = |v| |w| cos(theta) * * @param object Math_Vector2 or MathVector3 (or subclass) $v1 * @param object Math_Vector2 or MathVector3 (or subclass) $v2 * @return mixed the angle between vectors (float, in radians) on success * @throws InvalidArgumentException * * @see isVector2() * @see isVector3() * @see dotProduct() */ public static function angleBetween($v1, $v2) { if (Math_VectorOp::isVector2($v1) && Math_VectorOp::isVector2($v2) || Math_VectorOp::isVector3($v1) && Math_VectorOp::isVector3($v2)) { $v1->normalize(); $v2->normalize(); return acos(Math_VectorOp::dotProduct($v1, $v2)); } throw new InvalidArgumentException("Vectors must be both of the same type"); }