/**************************************************************************** ** ** This file is part of the LibreCAD project, a 2D CAD program ** ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl) ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved. ** ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file gpl-2.0.txt included in the ** packaging of this file. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ** ** This copyright notice MUST APPEAR in all copies of the script! ** **********************************************************************/ #include #include #include #include "rs_vector.h" #include "rs_debug.h" #include "rs_math.h" #include "lc_rect.h" #ifdef EMU_C99 #include "emu_c99.h" /* remainder() */ #endif /** * Constructor for a point with given coordinates. */ RS_Vector::RS_Vector(double vx, double vy, double vz): x(vx) ,y(vy) ,z(vz) ,valid(true) { } /** * Constructor for a unit vector with given angle */ RS_Vector::RS_Vector(double angle): x(cos(angle)) ,y(sin(angle)) ,valid(true) { } /** * Constructor for a point with given valid flag. * * @param valid true: a valid vector with default coordinates is created. * false: an invalid vector is created */ RS_Vector::RS_Vector(bool valid): valid(valid) { } RS_Vector::operator bool() const { return valid; } /** * Sets to a unit vector by the direction angle */ void RS_Vector::set(double angle) { x = cos(angle); y = sin(angle); z = 0.; valid = true; } /** * Sets a new position for the vector. */ void RS_Vector::set(double vx, double vy, double vz) { x = vx; y = vy; z = vz; valid = true; } /** * Sets a new position for the vector in polar coordinates. */ void RS_Vector::setPolar(double radius, double angle) { x = radius * cos(angle); y = radius * sin(angle); z = 0.0; valid = true; } RS_Vector RS_Vector::polar(double rho, double theta){ return {rho*cos(theta), rho*sin(theta), 0.}; } /** * @return The angle from zero to this vector (in rad). */ double RS_Vector::angle() const { return RS_Math::correctAngle(atan2(y,x)); } /** * @return The angle from this and the given coordinate (in rad). */ double RS_Vector::angleTo(const RS_Vector& v) const { if (!valid || !v.valid) return 0.0; return (v-(*this)).angle(); } /** * @return The angle from between two vectors using the current vector as the center * return 0, if the angle is not well defined */ double RS_Vector::angleBetween(const RS_Vector& v1, const RS_Vector& v2) const { if (!valid || !v1.valid || !v2.valid) return 0.0; RS_Vector const vStart(v1 - (*this)); RS_Vector const vEnd(v2 - (*this)); return RS_Math::correctAngle( atan2( vStart.x*vEnd.y-vStart.y*vEnd.x, vStart.x*vEnd.x+vStart.y*vEnd.y)); } /** * @return Magnitude (length) of the vector. */ double RS_Vector::magnitude() const { double ret(0.0); // Note that the z coordinate is also needed for 2d // (due to definition of crossP()) if (valid) ret = hypot(hypot(x, y), z); return ret; } /** * @return square of vector length */ double RS_Vector::squared() const { // Note that the z coordinate is also needed for 2d // (due to definition of crossP()) if (valid) return x*x + y*y + z*z; return RS_MAXDOUBLE; } /** * @return square of vector length */ double RS_Vector::squaredTo(const RS_Vector& v1) const { if (valid && v1.valid) { return (*this - v1).squared(); } return RS_MAXDOUBLE; } /** * */ RS_Vector RS_Vector::lerp(const RS_Vector& v, double t) const { return {x+(v.x-x)*t, y+(v.y-y)*t}; } /** * @return The distance between this and the given coordinate. */ double RS_Vector::distanceTo(const RS_Vector& v) const { if (!valid || !v.valid) { return RS_MAXDOUBLE; } else { return (*this-v).magnitude(); } } /** * @return true is this vector is within the given range. */ bool RS_Vector::isInWindow(const RS_Vector& firstCorner, const RS_Vector& secondCorner) const { if (!valid) return false; return LC_Rect{firstCorner, secondCorner}.inArea(*this); } /** * @return true is this vector is within the given range * of ordered vectors */ bool RS_Vector::isInWindowOrdered(const RS_Vector& vLow, const RS_Vector& vHigh) const { if(!valid) return false; return (x>=vLow.x && x<=vHigh.x && y>=vLow.y && y<=vHigh.y); } /** * move to the closest integer point */ RS_Vector RS_Vector::toInteger() { x = rint(x); y = rint(y); return *this; } /** * Moves this vector by the given offset. Equal to the operator +=. */ RS_Vector RS_Vector::move(const RS_Vector& offset) { *this+=offset; return *this; } /** * Rotates this vector around 0/0 by the given angle. */ RS_Vector RS_Vector::rotate(double ang) { rotate(RS_Vector{ang}); return *this; } /** * Rotates this vector around 0/0 by the given vector * if the vector is a unit, then, it's the same as rotating around * 0/0 by the angle of the vector */ RS_Vector RS_Vector::rotate(const RS_Vector& angleVector) { double x0 = x * angleVector.x - y * angleVector.y; y = x * angleVector.y + y * angleVector.x; x = x0; return *this; } /** * Rotates this vector around the given center by the given angle. */ RS_Vector RS_Vector::rotate(const RS_Vector& center, double ang) { *this = center + (*this-center).rotate(ang); return *this; } RS_Vector RS_Vector::rotate(const RS_Vector& center, const RS_Vector& angleVector) { *this = center + (*this-center).rotate(angleVector); return *this; } /** * Scales this vector by the given factors with 0/0 as center. */ RS_Vector RS_Vector::scale(double factor) { x *= factor; y *= factor; return *this; } /** * Scales this vector by the given factors with 0/0 as center. */ RS_Vector RS_Vector::scale(const RS_Vector& factor) { x *= factor.x; y *= factor.y; return *this; } RS_Vector RS_Vector::scale(const RS_Vector& factor) const{ return {x*factor.x, y*factor.y}; } /** * Scales this vector by the given factors with the given center. */ RS_Vector RS_Vector::scale(const RS_Vector& center, const RS_Vector& factor) { *this = center + (*this-center).scale(factor); return *this; } /** * Mirrors this vector at the given axis, defined by two points on axis. */ RS_Vector RS_Vector::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) { RS_Vector direction(axisPoint2-axisPoint1); double a= direction.squared(); RS_Vector ret(false); if(a RS_TOLERANCE && fabs(v.y)>RS_TOLERANCE) return {x / v.x, y / v.y, std::isnormal(v.z)?z / v.z:z}; return *this; } /** * binary * operator. */ RS_Vector RS_Vector::operator * (double s) const { return {x * s, y * s, z * s}; } /** * binary / operator. */ RS_Vector RS_Vector::operator / (double s) const { if(fabs(s)> RS_TOLERANCE) return {x / s, y / s, z / s}; return *this; } /** * unary - operator. */ RS_Vector RS_Vector::operator - () const { return {-x, -y, -z}; } /** * Scalarproduct (dot product). */ double RS_Vector::dotP(const RS_Vector& v1) const { return x*v1.x+y*v1.y; } double RS_Vector::dotP(const RS_Vector& v1, const RS_Vector& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } /** * Get position of \p pos in line \p start -> \p end, * as a factor of line length. * * @param start Start point of the line * @param end End point of the line * @param pos Point to calculate * @return double factor of line length, * factor == 0.0 : \p pos is same as start point * factor == 1.0 : \p pos is same as end point * factor < 0.0 : \p pos is in opposite direction * factor > 1.0 : \p pos is behind end point * factor > 0.0 and < 1.0 : \p pos is somewhere between start and end */ double RS_Vector::posInLine(const RS_Vector& start, const RS_Vector& end, const RS_Vector& pos) { RS_Vector dirEnd {end - start}; RS_Vector dirPos {pos - start}; double lenSquared {dirEnd.squared()}; if( RS_TOLERANCE2 > lenSquared ) { // line too short return start.distanceTo( pos); } return dotP( dirPos, dirEnd) / lenSquared; } /** switch x,y for all vectors */ RS_Vector RS_Vector::flipXY(void) const{ return {y, x}; } /** * += operator. Assert: both vectors must be valid. */ RS_Vector RS_Vector::operator += (const RS_Vector& v) { x += v.x; y += v.y; z += v.z; return *this; } /** * -= operator */ RS_Vector RS_Vector::operator -= (const RS_Vector& v) { x -= v.x; y -= v.y; z -= v.z; return *this; } RS_Vector RS_Vector::operator *= (const RS_Vector& v) { x *= v.x; y *= v.y; z *= v.z; return *this; } RS_Vector RS_Vector::operator /= (const RS_Vector& v) { if (fabs(v.x)> RS_TOLERANCE && fabs(v.y)>RS_TOLERANCE){ x /= v.x; y /= v.y; if (std::isnormal(v.z)) z /= v.z; } return *this; } /** * *= operator */ RS_Vector RS_Vector::operator *= (double s) { x *= s; y *= s; z *= s; return *this; } /** * /= operator */ RS_Vector RS_Vector::operator /= (double s) { if(fabs(s)>RS_TOLERANCE) { x /= s; y /= s; z /= s; } return *this; } /** * == operator */ bool RS_Vector::operator == (const RS_Vector& v) const { return (x==v.x && y==v.y && z==v.z && valid && v.valid); } bool RS_Vector::operator == (bool valid) const { return this->valid == valid; } bool RS_Vector::operator != (bool valid) const { return this->valid != valid; } /** * @return A vector with the minimum components from the vectors v1 and v2. * These might be mixed components from both vectors. */ RS_Vector RS_Vector::minimum (const RS_Vector& v1, const RS_Vector& v2) { if (!v2) return v1; if (!v1) return v2; return {std::min(v1.x, v2.x), std::min(v1.y, v2.y), std::min(v1.z, v2.z) }; } /** * @return A vector with the maximum values from the vectors v1 and v2 */ RS_Vector RS_Vector::maximum (const RS_Vector& v1, const RS_Vector& v2) { if (!v2) return v1; if (!v1) return v2; return {std::max(v1.x, v2.x), std::max(v1.y, v2.y), std::max(v1.z, v2.z) }; } /** * @return Cross product of two vectors. * we don't need cross product for 2D vectors */ RS_Vector RS_Vector::crossP(const RS_Vector& v1, const RS_Vector& v2) { return {v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x}; } /** * Constructor for no solution. */ RS_VectorSolutions::RS_VectorSolutions(): vector(0) ,tangent(false) { } RS_VectorSolutions::RS_VectorSolutions(const std::vector& l): vector( l.begin(), l.end()) ,tangent(false) { } /** * Constructor for num solutions. */ RS_VectorSolutions::RS_VectorSolutions(int num): vector(num, RS_Vector(false)) ,tangent(false) { } RS_VectorSolutions::RS_VectorSolutions(std::initializer_list const& l): vector(l) ,tangent(false) { } /** * Allocates 'num' vectors. */ void RS_VectorSolutions::alloc(size_t num) { if(num<=vector.size()){ vector.resize(num); }else{ const std::vector v(num - vector.size()); vector.insert(vector.end(), v.begin(), v.end()); } } RS_Vector RS_VectorSolutions::get(size_t i) const { if(i& RS_VectorSolutions::getVector() const { return vector; } std::vector::const_iterator RS_VectorSolutions::begin() const { return vector.begin(); } std::vector::const_iterator RS_VectorSolutions::end() const { return vector.end(); } std::vector::iterator RS_VectorSolutions::begin() { return vector.begin(); } std::vector::iterator RS_VectorSolutions::end() { return vector.end(); } void RS_VectorSolutions::push_back(const RS_Vector& v) { vector.push_back(v); } void RS_VectorSolutions::removeAt(const size_t i){ if (vector.size()> i) vector.erase(vector.begin()+i); } RS_VectorSolutions& RS_VectorSolutions::push_back(const RS_VectorSolutions& v) { vector.insert(vector.end(), v.begin(), v.end()); return *this; } /** * Sets the solution i to the given vector. * If i is greater than the current number of solutions available, * nothing happens. */ void RS_VectorSolutions::set(size_t i, const RS_Vector& v) { if (iprint(RS_Debug::D_ERROR, "set member in vector in RS_VectorSolutions: out of range, %d to size of %d", i,vector.size()); for(size_t j=vector.size();j<=i;++j) vector.push_back(v); } } /** * Sets the tangent flag. */ void RS_VectorSolutions::setTangent(bool t) { tangent = t; } /** * @return true if at least one of the solutions is a double solution * (tangent). */ bool RS_VectorSolutions::isTangent() const { return tangent; } /** * Rotates all vectors around (0,0) by the given angle. */ void RS_VectorSolutions::rotate(double ang) { RS_Vector angleVector(ang); for (auto& vp: vector) { if (vp.valid) { vp.rotate(angleVector); } } } /** * Rotates all vectors around (0,0) by the given angleVector. */ void RS_VectorSolutions::rotate(const RS_Vector& angleVector) { for (auto& vp: vector) { if (vp.valid) { vp.rotate(angleVector); } } } /** * Rotates all vectors around the given center by the given angle. */ void RS_VectorSolutions::rotate(const RS_Vector& center, double ang) { const RS_Vector angleVector(ang); for (auto& vp: vector) { if (vp.valid) { vp.rotate(center,angleVector); } } } void RS_VectorSolutions::rotate(const RS_Vector& center, const RS_Vector& angleVector) { for (auto& vp: vector) { if (vp.valid) { vp.rotate(center, angleVector); } } } /** * Move all vectors around the given center by the given vector. */ void RS_VectorSolutions::move(const RS_Vector& vp) { for (RS_Vector& v: vector) { if (v.valid) { v.move(vp); } } } /** * Scales all vectors by the given factors with the given center. */ void RS_VectorSolutions::scale(const RS_Vector& center, const RS_Vector& factor) { for (auto& vp: vector) { if (vp.valid) { vp.scale(center, factor); } } } void RS_VectorSolutions::scale( const RS_Vector& factor) { for (auto& vp: vector) { if (vp.valid) { vp.scale(factor); } } } /** * @return vector solution which is the closest to the given coordinate. * dist will contain the distance if it doesn't point to NULL (default). */ RS_Vector RS_VectorSolutions::getClosest(const RS_Vector& coord, double* dist, size_t* index) const { double curDist{0.}; double minDist = RS_MAXDOUBLE; RS_Vector closestPoint{false}; int pos(0); for (size_t i=0; i= 0) i=counts; std::for_each(vector.begin(), vector.begin() + i, [&ret, &coord](RS_Vector const& vp) { if(vp.valid) { double d=(coord - vp).squared(); if(d