I am using a class to represent a rotation angle...
class RotationAngle
{
protected:
float cosval;
float sinval;
__forceinline RotationAngle(float c, float s) { cosval = c; sinval = s; }
public:
__forceinline RotationAngle(float radians) { Set(radians); }
__forceinline RotationAngle(const RotationAngle& r) { Set(r); }
__forceinline void Set(float radians) { cosval = cos(radians); sinval = sin(radians); }
__forceinline void Set(const RotationAngle& r) { cosval = r.cosval; sinval = r.sinval; }
__forceinline float Cos() const { return cosval; }
__forceinline float Sin() const { return sinval; }
__forceinline RotationAngle& operator = (float radians) { Set(radians); return *this; }
__forceinline int operator == (const RotationAngle& r) const { return (r.cosval == cosval) && (r.sinval == sinval); }
__forceinline int operator != (const RotationAngle& r) const { return (r.cosval != cosval) || (r.sinval != sinval); }
};
I use this so I can add an angle to a point or line using an operator, and have the sine and cosine pre-calculated. It would be redundant to recalculate the sine and cosine of the angles every time the point was rotated by the same angle.
class Point
{
public:
float x;
float y;
__forceinline void Set(float sx, float sy) { x = sx; y = sy; }
__forceinline void Rotate(const RotationAngle& a) { Set(x * a.Cos() - y * a.Sin(), x * a.Sin() + y * a.Cos()); }
__forceinline void operator += (const RotationAngle& a) { Rotate(a); }
};
I would like to include a -= operator to use the negative representation of a RotationAngle (ie counterclockwise rotation). Will simply using -cos and -sin work? I'm not sure considering the formula for adding sin/cos is not as straightforward. Is there a quick way to accomplish this?
Negating an angle will negate its sine, but leave its cosine unchanged. So it might make sense to define a unary negation:
RotationAngle operator-() const {return RotationAngle(cosval, -sinval);}
and then define -= in terms of that
void operator -= (const RotationAngle& a) { Rotate(-a); }
Related
I am experimenting with lambda functions and managed to recreate a "get" functionality in C++. I can get the return value of a function without using parentheses. This is an example class, where I implement this:
using namespace std;
struct Vector2 {
float x;
float y;
float length = [&]()-> float {return sqrt(x * x + y * y); }();
float angle = [&]()-> float {return atan2(y, x); }();
Vector2() : x(0), y(0) {}
Vector2(float a, float b) : x(a), y(b) {}
~Vector2() {}
Vector2(Vector2& other) : x(other.x), y(other.y) {}
Vector2(Vector2&& other) = delete;
void operator =(Vector2&& other) noexcept{
x = other.x;
y = other.y;
}
};
int main()
{
Vector2 vec = Vector2(10, 17);
printf("%f\n%f\n%f\n%f\n", vec.x, vec.y, vec.length, vec.angle);
}
However, I am currently trying to also recreate the "set" functionality that C# has. But I'm failing. I tried to add this:
void angle = [&](float a)->void {
float l = length;
x = cos(a) * l;
y = sin(a) * l;
};
But am getting "Incomplete type is not allowed" error. I'm not sure if that's how it should look, even if I wasn't getting the error. Is it even possible to recreate the "set" functionality C# has in C++?
I know that I can just use a method SetAngle(float a){...}, but that's not really the point.
TL;DR: Don't
What you have isn't a getter, it's just a normal data member that's calculated once when the object is initialized.
In general, C++ doesn't support C#-style properties. The usual C++-style solution is to just use a pair of member functions (and maybe a data member, if you need to save the value separately), i.e.
struct Vector2 {
// ...
float length() const { return sqrt(x * x + y * y); }
void length(float l) {
float angle = angle();
float new_x = l * cos(angle);
float new_y = l * sin(angle);
x = new_x;
y = new_y;
}
// ...
};
You can get something close to a C#-style property, but you'll always run into edge-cases where they don't work perfectly. For example, here's something that will work in many cases:
template <typename T>
class Property
{
private:
std::function<T()> getter_;
std::function<void(const T&)> setter_;
public:
Property(std::function<T()> getter, std::function<void(const T&)> setter)
: getter_{getter},
setter_{setter}
{}
operator T()
{
return getter_();
}
const T& operator=(const T& val)
{
setter_(val);
return val;
}
};
struct Vector2
{
float x;
float y;
Property<float> length{
[this]() { return sqrt(x * x + y * y); },
[this](float l) {
float new_x = l * cos(angle);
float new_y = l * sin(angle);
x = new_x;
y = new_y;
}
}
Property<float> angle{
[this]() { return atan2(y, x); },
[this](float a) {
float l = length;
x = cos(a) * l;
y = sin(a) * l;
}
}
// ...
};
int main() {
Vector2 v;
v.x = 1;
v.y = 1;
v.angle = std::numbers::pi / 2;
std::cout << "(" << v.x << ", " << v.y << ")\n";
}
But this still falls apart in the edge cases, especially when you mix it with templates and/or auto type-deduction. For instance:
Vector2 v;
v.x = 1;
v.y = 1;
auto old_angle = v.angle;
v.angle = std::numbers::pi / 2;
// oops, this prints pi/2, not pi/4 like you probably expected
// because old_angle isn't a float, it's a Property<float> that
// references v
std::cout << old_angle << '\n';
Note also that there's a bug here. Consider this:
int main() {
Vector2 v1;
v1.x = 1;
v1.y = 1;
Vector2 v2 = v1;
v2.angle = std::numbers::pi / 2;
// Oops, assigning to v2.angle modified v1
std::cout << "(" << v1.x << ", " << v1.y << ")\n";
}
You could work around these issues by making Property non-copyable, but then you force any class that uses it to implement a custom copy-constructor. Also, while that would make the auto case "safe", it does so by turning it into a compile error. Still not ideal.
I agree with Miles. This is not the greatest idea, because it's unnatural for C++ developers, and you should write code that is first and foremost easy to read.
However, as an engineering challenge, here's a possible implementation:
#include <math.h>
#include <iostream>
template <typename T>
class Member
{
public:
operator T() const { return _value; }
void operator =(const T& value) const { _value = value; } void operator =(T&& value) { _value = std::move(value); }
private:
T _value;
};
class Angle
{
public:
Angle(const Member<float>& x, const Member<float>& y) :
_x(x), _y(y) {}
operator float() const { return atan2(_y, _x); }
private:
const Member<float>& _x, _y;
};
class Obj
{
public:
Member<float> x, y;
Angle angle;
Obj() : angle(this->x, this->y) {}
};
int main()
{
Obj o;
o.x = 3;
o.y = 5;
std::cout << o.x << ", " << o.y << " -> " << o.angle << std::endl;
}
While other solutions also seem to be possible, this one seems to be the most elegant :P
using namespace std;
struct Vector2 {
float x;
float y;
float init_length = [&]()-> float {return sqrt(x * x + y * y); }();
float init_angle = [&]()-> float {return atan2(y, x); }();
__declspec(property(get = GetAngle, put = SetAngle)) float angle;
__declspec(property(get = GetLength, put = SetLength)) float length;
Vector2() : x(0), y(0) {}
Vector2(float a, float b) : x(a), y(b) {}
~Vector2() {}
Vector2(Vector2& other) : x(other.x), y(other.y) {}
Vector2(Vector2&& other) = delete;
void operator =(Vector2&& other) = delete;
void Display() {
printf("%f\n%f\n%f\n%f\n\n", x, y, length, angle);
}
float GetLength() {
return sqrt(x * x + y * y);
}
float GetAngle() {
return atan2(y, x);
}
void SetLength(float l) {
float a = GetAngle();
x = cos(a) * l;
y = sin(a) * l;
}
void SetAngle(float a) {
float l = GetLength();
x = cos(a) * l;
y = sin(a) * l;
}
};
int main()
{
Vector2 vec = Vector2(10, 17);
vec.Display();
vec.length = 5;
vec.Display();
}
I have done the algorithm the expected output is :
p00 - p01,
p01 - p03 ,
p03 - p10,
p10 - p12 ,
p12 - p00
But I get this instead:
Convex hull:
p00 - p01
p01 - p03
p03 - p05
p05 - p10
p10 - p00
Points:
p00: (-5,-6)
p01: (6,-4)
p02: (5.5,-3)
p03: (8,0)
p04: (5,0)
p05: (4,2)
p06: (1,3)
p07: (0,2)
p08: (-1,1)
p09: (-1.5,2)
p10: (-1.5,6)
p11: (-5.5,1.5)
p12: (-8,-1)
I have been trying so long to get it right but some how I can't. Can anyone help? I am using C++
Below is my code:
I have 3 classes Vector2D, Point2D and Point2DSet my Graham Scan Implementation is in the buildConvexHull function in the Point2DSet.
Vector2D.cpp
#include "Vector2D.h"
Vector2D::Vector2D(double aX, double aY): fX(aX), fY(aY){ }
void Vector2D::setX(double aX){ fX = aX;}
double Vector2D::getX() const { return fX; }
void Vector2D::setY(double aY) { fY = aY;}
double Vector2D::getY() const { return fY; }
Vector2D Vector2D::operator+(const Vector2D& aRHS) const
{
return (fX + aRHS.fX, fY + aRHS.fY);
}
Vector2D Vector2D::operator-(const Vector2D& aRHS) const
{
return (fX - aRHS.fX, fY - aRHS.fY);
}
double Vector2D::magnitude() const
{
return sqrt((fX * fX) + (fY * fY));
}
double Vector2D::direction() const
{
return atan(fY/fX);
}
double Vector2D::dot(const Vector2D& aRHS) const
{
return (this->getX() * aRHS.getX()) + (this->getY() * aRHS.getY());
}
double Vector2D::cross(const Vector2D& aRHS) const
{
return (this->getX() * aRHS.getY()) - (aRHS.getX() * this->getY());
}
double Vector2D::angleBetween(const Vector2D& aRHS) const
{
double dntmr = magnitude() * aRHS.magnitude();
if (dntmr > 0.0)
{
return acos(this->dot(aRHS) / (this->magnitude() * aRHS.magnitude()));
}
return acos(1.0/1.0);
}
std::ostream& operator<<(std::ostream& aOutStream, const Vector2D& aObject)
{
aOutStream << " ( " << aObject.fX << ", " << aObject.fY << " )\n";
return aOutStream;
}
std::istream& operator>>(std::istream& aInStream, Vector2D& aObject)
{
aInStream >> aObject.fX;
aInStream >> aObject.fY;
return aInStream;
}
Point2D
#include "Point2D.h"
static const Point2D gCoordinateOrigin;
// Private function gets direction in reference to aOther
double Point2D::directionTo(const Point2D& aOther) const
{
return (aOther.fPosition - fPosition).direction();
}
// Private Function to get magnitude in reference to aOther
double Point2D::magnitudeTo(const Point2D& aOther) const
{
return (aOther.fPosition - fPosition).magnitude();
}
Point2D::Point2D() : fId(" "), fPosition(0,0), fOrigin(&gCoordinateOrigin) { }
Point2D::Point2D(const std::string& aId, double aX, double aY) : fId(aId), fPosition(aX,aY), fOrigin(&gCoordinateOrigin) { }
Point2D::Point2D(std::istream &aIStream) : fOrigin(&gCoordinateOrigin)
{
aIStream >> fId >> fPosition;
}
const std::string& Point2D::getId() const { return fId; }
void Point2D::setX(const double& aX) { fPosition.setX(aX); }
void Point2D::setY(const double& aY) { fPosition.setY(aY); }
const double Point2D::getX() const { return fPosition.getX(); }
const double Point2D::getY() const { return fPosition.getY(); }
void Point2D::setOrigin(const Point2D& aPoint) { fOrigin = &aPoint;}
Vector2D Point2D::operator-(const Point2D& aRHS) const
{
return (fPosition - aRHS.fPosition);
}
// Return Direction with reference to origin
double Point2D::direction() const
{
return fOrigin->directionTo(*this);
}
// Return Direction with reference to origin
double Point2D::magnitude() const
{
return fOrigin->magnitudeTo(*this);;
}
bool Point2D::isCollinear(const Point2D& aOther) const
{
if (fPosition.cross(aOther.fPosition) == 0)
{
return true;
}
return false;
}
// Check to see if the point is Clockwise or not
bool Point2D::isClockwise(const Point2D& aP0, const Point2D& aP2) const
{
double val = (fPosition.getY() - aP0.fPosition.getY()) * (aP2.fPosition.getX() - fPosition.getX()) -
(fPosition.getX() - aP0.fPosition.getX()) * (aP2.fPosition.getY() - fPosition.getY());
double val2 = fPosition.angleBetween(aP2.fPosition) - fPosition.angleBetween(aP0.fPosition);
if (val < 0 )
{
return false;
}
return true;
}
bool Point2D::operator<(const Point2D& aRHS) const
{
if (fPosition.getY() < aRHS.getY())
{
return true;
}
return false;
}
const Point2D& Point2D::getOrigin() const { return *fOrigin;}
std::ostream& operator<<(std::ostream& aOStream, const Point2D& aObject)
{
aOStream << aObject.fId << " : " << aObject.fPosition;
return aOStream;
}
std::istream& operator>>(std::istream& aIStream, Point2D& aObject)
{
aIStream >> aObject.fId >> aObject.fPosition;
return aIStream;
}
Point2DSet
#include "Point2DSet.h"
#include <fstream>
#include <stdexcept>
#include <algorithm>
void Point2DSet::add(const Point2D& aPoint)
{
fPoints.push_back(aPoint);
}
void Point2DSet::add(Point2D&& aPoint)
{
fPoints.push_back(aPoint);
}
void Point2DSet::removeLast()
{
fPoints.pop_back();
}
bool Point2DSet::doesNotTurnLeft(const Point2D& aPoint) const
{
return fPoints[size()-1].isClockwise(fPoints[size()-2],aPoint);
}
// Comparator function for Stable_sort
bool orderByCoordinates(const Point2D& aLeft, const Point2D& aRight)
{
return aLeft < aRight;
}
//Comparator function for Stable_sort
bool orderByPolarAngle(const Point2D& aLHS, const Point2D& aRHS)
{
if (aLHS.isCollinear(aRHS))
{
return aLHS.magnitude() > aRHS.magnitude();
}
return aLHS.direction() < aRHS.direction();
}
void Point2DSet::populate(const std::string& aFileName)
{
std::ifstream INPUT(aFileName);
//std::ifstream INPUT("Pointers.txt");
std::string id;
double x;
double y;
while (INPUT >> id >> x >> y)
{
Point2D z(id, x, y);
add(z);
}
INPUT.close();
}
void Point2DSet::buildConvexHull(Point2DSet& aConvexHull)
{
aConvexHull.clear();
sort(orderByCoordinates);
sort(orderByPolarAngle);
aConvexHull.add(fPoints[0]); // Origin (Smallest y-coordinate)
aConvexHull.add(fPoints[1]); //
//aConvexHull.add(fPoints[2]);
if (fPoints[2].isCollinear(fPoints[1])) {
aConvexHull.add(fPoints[2]);
}
//*/
for(size_t i = 3; i < size(); i++)
{
if (fPoints[i - 1].isCollinear(fPoints[i]))
{
continue; //i++;
}
if(aConvexHull.doesNotTurnLeft(fPoints[i]))
{
aConvexHull.removeLast();
}
aConvexHull.add(fPoints[i]);
}//*/
}
size_t Point2DSet::size() const
{
return fPoints.size();
}
void Point2DSet::clear()
{
fPoints.clear();
}
void Point2DSet::sort(Comparator aComparator)
{
stable_sort(fPoints.begin(), fPoints.end(), aComparator);
}
const Point2D& Point2DSet::operator[](size_t aIndex) const
{
return fPoints[aIndex];
}
Point2DSet::Iterator Point2DSet::begin() const
{
return fPoints.begin();
}
Point2DSet::Iterator Point2DSet::end() const
{
return fPoints.end();
}
Any other improvements are warmly welcome. Thank You!
There was a few issues in your code.
Let's start with Vector2D::direction(), you should use atan2, here the explanation why. After that we will be able to correctly sort the points.
Now the main algorithm. After a few changes it looks:
aConvexHull.clear();
// Get points with bigger magnitude first.
sort(orderByMagnitudeDescending);
sort(orderByPolarAngle);
// We want to have the lowest point as the first element.
rotatePointsByLowest();
aConvexHull.add(fPoints[0]); // Origin (Smallest y-coordinate)
aConvexHull.add(fPoints[1]);
for(size_t i = 2; i < size(); i++)
{
if (fPoints[i - 1].isCollinear(fPoints[i]))
{
continue; //i++;
}
// There should be a loop instead of an if statement.
while (aConvexHull.fPoints.size() > 2 && aConvexHull.doesNotTurnLeft(fPoints[i]))
{
aConvexHull.removeLast();
}
aConvexHull.add(fPoints[i]);
}//*/
The algorithm requires to find the lowest point and then traverse the rest of points according to their angle. I added a helper function Point2DSet::rotatePointsByLowest:
void Point2DSet::rotatePointsByLowest() {
auto lowestPoint = fPoints.begin();
for (auto iterator = fPoints.begin() + 1;iterator != fPoints.end(); iterator++) {
if (iterator->fPosition.fY < lowestPoint->fPosition.fY) {
lowestPoint = iterator;
} else if ((iterator->fPosition.fY == lowestPoint->fPosition.fY) && (iterator->fPosition.fX < lowestPoint->fPosition.fX)) {
lowestPoint = iterator;
}
}
std::rotate(fPoints.begin(), lowestPoint, fPoints.end());
}
There are more improvements that should be applied but I wanted to keep the changes minimal to show the issues causing the incorrect result.
Link for testing your project: https://onlinegdb.com/_ZXmQF2vJ
I am trying to figure out how to find the axis of class Square's axis as shown below? But I've been trying for hours and still did not managed to solve it. Can someone with their high level expertise show me the ropes to do it? Because both center function call and axis function call in main() does call the same x() and y(), hence brought me to a state of confusion. I know the inheritance of square from circle is weird. But it is what my school wants. Note: Main() CANNOT be modified! Thanks!
Output:
Square::axis test failed
8.87627 0.284967
3.82567 0.958537
Tests passed: 50%
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
class Object
{
public:
private:
float d;
public:
Object(float n) : d(n){}
Object(){}
float depth() const
{
return d;
}
struct PointType
{
float x2;
float y2;
PointType( float x1, float y1) :x2(x1),y2(y1){}
PointType(){}
float x()
{
return x2;
}
float y()
{
return y2;
}
PointType center()
{
return *this;
}
};
struct VectorType
{
float tx;
float ty;
VectorType( float tx1, float ty1) :tx(tx1),ty(ty1){}
VectorType( ){}
};
virtual ~Object()
{}
};
class Point :public Object
{
private:
PointType mpoint;
public:
Point(const PointType& pt, float& y1) :Object(y1), mpoint(pt) {}
Point(const PointType& pt):mpoint(pt){}
Point(){}
Point center() const
{
return *this;
}
float x()
{
return mpoint.x2;
}
float y()
{
return mpoint.y2;
}
virtual ~Point(){}
};
class Circle : public Point
{
private:
Object::PointType m_pt;
float r;
public:
Circle(const PointType pts, float rad, float dep)
: Point(m_pt,dep),m_pt(pts),r(rad) {}
Circle(const PointType pts, float rad):m_pt(pts),r(rad){}
Circle(){}
float radius() const
{
return r;
}
Circle center() const
{
return *this;
};
float x()
{
return m_pt.x2;
}
float y()
{
return m_pt.y2;
}
};
class Square: public Circle
{
private:
Object::PointType s_pt;
Object::VectorType v_pt;
float a=getRadius();
public:
Square(const PointType spts,const VectorType vpts,float depth) :
Circle(spts,a,depth),s_pt(spts),v_pt(vpts){}
Square(const Object::PointType& spts, const VectorType vpts):s_pt(spts),v_pt(vpts){}
Square axis() const
{
return Square(s_pt,v_pt,getRadius());
}
Square center() const
{
return *this;
}
float radius() const
{
return a;
}
float getRadius() const
{
float rad= sqrt(v_pt.tx * v_pt.tx + v_pt.ty * v_pt.ty);
return rad;
}
float x() const
{
return s_pt.x2;
// v_pt.tx/radius();
}
float y() const
{
return s_pt.y2;
// v_pt.ty/radius();
}
};
const float EPSILON = 1e-5f;
bool is_near(float x, float y)
{
return std::abs(x - y) < EPSILON;
}
float frand()
{
return 10.0f * float(rand()) / float(RAND_MAX);
}
int main()
{
srand(unsigned(time(0)));
int count = 0;
int max_count = 0;
float x = frand();
float y = frand();
float sx = frand();
float sy = frand();
float depth = frand();
Square square(Square::PointType(x, y), Square::VectorType(sx, sy), depth);
if (is_near(square.center().x(), x) &&
is_near(square.center().y(), y))
{
++count;
}
else
{
std::cout << " - Square::center test failed" << std::endl;
}
++max_count;
float radius = std::sqrt(sx * sx + sy * sy);
if (is_near(square.axis().x(), sx / radius) &&
is_near(square.axis().y(), sy / radius))
{
++count;
}
else
{
std::cout << " - Square::axis test failed" << std::endl;
}
++max_count;
std::cout << square.axis().x()<< " " << sx / radius<<std::endl;
std::cout << square.axis().y()<< " " << sy / radius<<std::endl;
int result = static_cast<int>(
100.0f * static_cast<float>(count) / static_cast<float>(max_count) + 0.5f
);
std::cout << "Tests passed: " << result << "%" << std::endl;
return result;
}
I'm having an issue with the inheritance of my operator overloading functions in my derived class.
#pragma once
#include "Math.hpp"
using namespace Math;
class Euler
{
public:
float x, y, z;
Euler();
Euler(const float &, const float &, const float &);
float &operator[](const char &) const;
Euler &operator=(const Euler &);
Euler &operator+=(const Euler &);
Euler &operator-=(const Euler &);
Euler &operator*=(const float &);
Euler &operator/=(const float &);
Euler operator+(const Euler &) const;
Euler operator-(const Euler &) const;
Euler operator*(const float &) const;
Euler operator/(const float &) const;
bool operator==(const Euler &) const;
bool operator!=(const Euler &) const;
bool IsValid() const;
void Clear();
};
inline Euler::Euler()
{
x = y = z = 0.f;
}
inline Euler::Euler(const float &_x, const float &_y, const float &_z)
{
x = _x;
y = _y;
z = _z;
}
inline float &Euler::operator[](const char &c) const
{
return ((float *)this)[c];
}
inline Euler &Euler::operator=(const Euler &e)
{
x = e.x;
y = e.y;
z = e.z;
return *this;
}
inline Euler &Euler::operator+=(const Euler &e)
{
x += e.x;
y += e.y;
z += e.z;
return *this;
}
inline Euler &Euler::operator-=(const Euler &e)
{
x -= e.x;
y -= e.y;
z -= e.z;
return *this;
}
inline Euler &Euler::operator*=(const float &e)
{
x *= e;
y *= e;
z *= e;
return *this;
}
inline Euler &Euler::operator/=(const float &e)
{
x /= e + M_FLT_EPSILON;
y /= e + M_FLT_EPSILON;
z /= e + M_FLT_EPSILON;
return *this;
}
inline Euler Euler::operator+(const Euler &e) const
{
return Euler(x + e.x, y + e.y, z + e.z);
}
inline Euler Euler::operator-(const Euler &e) const
{
return Euler(x - e.x, y - e.y, z - e.z);
}
inline Euler Euler::operator*(const float &f) const
{
return Euler(x * f, y * f, z * f);
}
inline Euler Euler::operator/(const float &f) const
{
return Euler(x / (f + M_FLT_EPSILON), y / (f + M_FLT_EPSILON), z / (f + M_FLT_EPSILON));
}
inline bool Euler::operator==(const Euler &e) const
{
return e.x == x && e.y == y && e.z == z;
}
inline bool Euler::operator!=(const Euler &e) const
{
return e.x != x || e.y != y || e.z != z;
}
inline bool Euler::IsValid() const
{
using namespace std;
return isfinite(x) && isfinite(y) && isfinite(z);
}
inline void Euler::Clear()
{
x = y = z = 0.f;
}
class Vector : public Euler
{
public:
using Euler::Euler;
void Rotate(const Angle &);
void Rotate2D(const float &);
float Length() const;
float LengthSqr() const;
float Length2D() const;
float Length2DSqr() const;
float DistTo(const Vector &) const;
float DistToSqr(const Vector &) const;
};
inline float Vector::Length() const
{
return Sqrt((x * x) + (y * y) + (z * z));
}
inline float Vector::LengthSqr() const
{
return (x * x) + (y * y) + (z * z);
}
inline float Vector::Length2D() const
{
return Sqrt((x * x) + (y * y));
}
inline float Vector::Length2DSqr() const
{
return (x * x) + (y * y);
}
inline float Vector::DistTo(const Vector &v) const
{
return (*this - v).Length();
}
inline float Vector::DistToSqr(const Vector &v) const
{
return (*this - v).LengthSqr();
}
The code shown below contains an error in the distto and DistToSqr function wherein it calculates (*this - v) as the superclass Euler and therefore cannot find the length function.
I was wondering why this would be as this code compiles on my laptop and not my desktop.
I'd be grateful if anyone could show me why this code doesn't work and what is the best course in fixing it.
It seems like doing this is a plausible way to fix it.
inline float Vector::DistTo(const Vector &v) const
{
Vector tmp = (*this - v);
return tmp.Length();
}
Still wondering if this is the best option to fix it though.
I'm going to assume that it is likely that Euler is always used as a base type of a more specific type? If so, you can might be able to solve this using the CRTP pattern. Here is a subset of your code in this style:
#include <cmath>
template <typename Derived>
class Euler
{
public:
float x, y, z;
Euler();
Euler(const float &, const float &, const float &);
template <typename T>
Derived & operator-=(Euler<T> const &);
template <typename T>
Derived operator-(Euler<T> const &) const;
private:
Derived & derived() const {
return static_cast<Derived &>(*this);
}
Derived const & derived() {
return static_cast<Derived const &>(*this);
}
};
template <typename Derived>
Euler<Derived>::Euler() : x(0), y(0), z(0)
{}
template <typename Derived>
Euler<Derived>::Euler(const float &_x, const float &_y, const float &_z)
: x(_x), y(_y), z(_z)
{}
template <typename Derived>
template <typename T>
Derived & Euler<Derived>::operator-=(Euler<T> const & e)
{
x -= e.x;
y -= e.y;
z -= e.z;
return derived();
}
template <typename Derived>
template <typename T>
Derived Euler<Derived>::operator-(Euler<T> const & e) const
{
return Derived(x - e.x, y - e.y, z - e.z);
}
class Vector : public Euler<Vector>
{
public:
using Euler<Vector>::Euler;
float Length() const;
float DistTo(const Vector &) const;
};
inline float Vector::Length() const
{
return std::sqrt(x * x + y * y + z * z);
}
inline float Vector::DistTo(const Vector & v) const
{
return (*this - v).Length();
}
We have found that HP's printer drivers fail to handle PlgBlt()s properly for many of their printers.
Our intention is to handle any orthogonal rotations ourselves, and only have the printer handle scale and translation (which it seems to handle correctly).
I have a 2D matrix available to me at the point in code where I am about to "draw" the bitmap to the printer's DC.
I am weak as hell in math, and I understand just enough about matrix math to use them to transform 2D or 3D coordinates. But the underlying math is opaque to me.
So, what I need is to detect if a given 2D matrix is orthogonal in its transformation (the rotation aspect anyway). I suppose another way to ask this question would be: How do I get the rotation vector back out of the 2D matrix? If I knew the angle of rotation in radians or degrees, I could say whether its orthogonal or not (0,90,180,270).
Presumably, the code is generic on this subject, but below is the basics of the code we use, in case that helps:
typedef double ThreeByThreeMatrix[3][3]; // 3x3 for an X, Y coordinate transformation matrix
Then there's a wrapper for that which handles most obvious operations:
class Simple2DTransform
{
public:
////////////////////////////////////////////////////
// Construction
////////////////////////////////////////////////////
// we always begin life as an identity matrix (you can then apply scale, skew, etc.)
Simple2DTransform()
{
Reset();
}
////////////////////////////////////////////////////
// Operators
////////////////////////////////////////////////////
bool operator == (const Simple2DTransform & rhs) const
{
return memcmp(m_matrix, rhs.m_matrix, sizeof(m_matrix)) == 0;
}
Simple2DTransform & operator *= (const Simple2DTransform & rhs)
{
return *this = GetCrossProduct(rhs);
}
////////////////////////////////////////////////////
// Setup
////////////////////////////////////////////////////
// reset to the identity matrix
Simple2DTransform & Reset()
{
memcpy(m_matrix, GetIdentityMatrix(), sizeof(m_matrix));
return *this;
}
// combine with the specified translation
Simple2DTransform & Translate(double x_shift, double y_shift)
{
Simple2DTransform transform;
translate(x_shift, y_shift, transform.m_matrix);
return *this *= transform;
}
// combine with the specified operations (these are cumulative operations, so rotating twice by 1 degree gives a total of 2 degrees rotation)
Simple2DTransform & Rotate(double radians)
{
Simple2DTransform transform;
rotate(radians, transform.m_matrix);
return *this *= transform;
}
// apply a heterogeneous scale factor
Simple2DTransform & Scale(double x_scale, double y_scale)
{
Simple2DTransform transform;
scale(x_scale, y_scale, transform.m_matrix);
return *this *= transform;
}
// apply a homogeneous scale factor
Simple2DTransform & Scale(double scale)
{
return Scale(scale, scale);
}
// apply a skew
Simple2DTransform & Skew(double skew)
{
Simple2DTransform transform;
skew_y(skew, transform.m_matrix);
return *this *= transform;
}
////////////////////////////////////////////////////
// Queries
////////////////////////////////////////////////////
// return the cross product of this and the given matrix
Simple2DTransform GetCrossProduct(const Simple2DTransform & rhs) const
{
Simple2DTransform result;
GEMM(m_matrix, rhs.m_matrix, result.m_matrix);
return result;
}
// returns the inverse of ourselves
Simple2DTransform GetInverse() const
{
// note: invert mucks with both matrices, so we use a copy of ourselves for it
Simple2DTransform original(*this), inverse;
invert(original.m_matrix, inverse.m_matrix);
return inverse;
}
// derivative values
double GetCoefficient(int i, int j) const
{
return m_matrix[i][j];
}
// return the cross product
double GetDeterminate() const
{
return m_matrix[0][0]*m_matrix[1][1] - m_matrix[0][1]*m_matrix[1][0];
}
// returns the square root of the determinate (this ignores heterogeneous scaling factors)
double GetScale() const
{
return sqrt(abs(GetDeterminate()));
}
// returns true if there is a scale factor
bool IsStretched() const
{
return (abs(abs(m_matrix[0][0]) - abs(m_matrix[1][1])) > 1.0e-7
|| abs(abs(m_matrix[0][1]) - abs(m_matrix[1][0])) > 1.0e-7);
}
// true if we're the identity matrix
bool IsIdentity() const
{
return memcmp(m_matrix, GetIdentityMatrix(), sizeof(m_matrix)) == 0;
}
// returns true if this represents the same transformation as the given subtable
bool IsSubtableEqual(const SUBTABLE * pSubTable) const
{
ASSERT(pSubTable);
if (abs(pSubTable->tran1 - m_matrix[0][0]) > 1.0e-7)
return false;
if (abs(pSubTable->tran2 - m_matrix[1][0]) > 1.0e-7)
return false;
if (abs(pSubTable->tran3 - m_matrix[0][1]) > 1.0e-7)
return false;
if (abs(pSubTable->tran4 - m_matrix[1][1]) > 1.0e-7)
return false;
return true;
}
////////////////////////////////////////////////////
// Application / Execution
////////////////////////////////////////////////////
void Transform(const SimplePoint & point, SimplePoint & newpoint) const
{
newpoint.x = point.x * m_matrix[0][0] + point.y * m_matrix[1][0] + m_matrix[2][0];
newpoint.y = point.x * m_matrix[0][1] + point.y * m_matrix[1][1] + m_matrix[2][1];
}
void Transform(SimplePoint & point) const
{
SimplePoint newpoint;
Transform(point, newpoint);
point = newpoint;
}
void Transform(const SimpleRect & rect, SimpleRect & newrect) const
{
newrect.minX = rect.minX * m_matrix[0][0] + rect.minY * m_matrix[1][0] + m_matrix[2][0];
newrect.minY = rect.minX * m_matrix[0][1] + rect.minY * m_matrix[1][1] + m_matrix[2][1];
newrect.maxX = rect.maxX * m_matrix[0][0] + rect.maxY * m_matrix[1][0] + m_matrix[2][0];
newrect.maxY = rect.maxX * m_matrix[0][1] + rect.maxY * m_matrix[1][1] + m_matrix[2][1];
}
void Transform(SimpleRect & rect) const
{
SimpleRect newrect;
Transform(rect, newrect);
rect = newrect;
}
void Transform(CPoint & point) const
{
SimplePoint newpoint(point);
Transform(newpoint);
point.x = (int)(newpoint.x > 0.0 ? newpoint.x + 0.5 : newpoint.x - 0.5);
point.y = (int)(newpoint.y > 0.0 ? newpoint.y + 0.5 : newpoint.y - 0.5);
}
void Transform(SimplePoint point, CPoint & transformed) const
{
Transform(point);
transformed.x = (int)(point.x > 0.0 ? point.x + 0.5 : point.x - 0.5);
transformed.y = (int)(point.y > 0.0 ? point.y + 0.5 : point.y - 0.5);
}
void Transform(CPoint point, SimplePoint & transformed) const
{
transformed = point;
Transform(transformed);
}
void Transform(double dx, double dy, double & x, double & y) const
{
SimplePoint point(dx, dy);
Transform(point);
x = point.x;
y = point.y;
}
void Transform(double & x, double & y) const
{
SimplePoint point(x, y);
Transform(point);
x = point.x;
y = point.y;
}
SimplePoint GetTransformed(SimplePoint point) const
{
Transform(point);
return point;
}
CPoint GetTransformed(CPoint point) const
{
Transform(point);
return point;
}
SimpleRect GetTransformed(const CRect & rect) const
{
return SimpleRect(GetTransformed(SimplePoint(rect.left, rect.bottom)), GetTransformed(SimplePoint(rect.right, rect.top)));
}
SimpleRect GetTransformed(double x1, double y1, double x2, double y2) const
{
return SimpleRect(GetTransformed(SimplePoint(x1, y1)), GetTransformed(SimplePoint(x2, y2)));
}
double GetTransformedX(double x, double y) const
{
return GetTransformed(SimplePoint(x, y)).x;
}
double GetTransformedY(double x, double y) const
{
return GetTransformed(SimplePoint(x, y)).y;
}
double GetTransformedX(int x, int y) const
{
return GetTransformed(SimplePoint(x, y)).x;
}
double GetTransformedY(int x, int y) const
{
return GetTransformed(SimplePoint(x, y)).y;
}
double GetTransformedX(const SimplePoint & point) const
{
return GetTransformed(point).x;
}
double GetTransformedY(const SimplePoint & point) const
{
return GetTransformed(point).y;
}
int GetTransformedIntX(double x, double y) const
{
CPoint point;
Transform(SimplePoint(x, y), point);
return point.x;
}
int GetTransformedIntY(double x, double y) const
{
CPoint point;
Transform(SimplePoint(x, y), point);
return point.y;
}
int GetTransformedIntX(const SimplePoint & point) const
{
CPoint pt;
Transform(point, pt);
return pt.x;
}
int GetTransformedIntY(const SimplePoint & point) const
{
CPoint pt;
Transform(point, pt);
return pt.y;
}
protected:
////////////////////////////////////////////////////
// Static Class Operations
////////////////////////////////////////////////////
static const ThreeByThreeMatrix & GetIdentityMatrix();
////////////////////////////////////////////////////
// Instance Variables
////////////////////////////////////////////////////
ThreeByThreeMatrix m_matrix;
};
Let A = (0, 0), B = (1, 0). Transform both through the matrix to get A' and B'. Measure the angle of the vector B' - A'. That should give you the angle.
To measure the angle of the vector, you can use atan2 (B'.y - A'.y, B'.x - A'.x)