Don't understand how QPoint is being calculated here. QPoint has x and y. I've tried running this with a custom struct but I get errors such as Invalid operands to binary expression.
Works:
void test(QPoint p0, QPoint p1, QPoint p2, QPoint p3) {
QPoint point;
for(double t = 0.0; t<=1.0; t+=0.001){
point = pow((1-t),3) * p0 + 3 * pow((1-t),2) * t * p1 + 3 * (1-t) * pow(t,2) * p2 + pow(t,3) * p3;
}
}
Doesn't work
struct Point {
int x;
int y;
};
void test(Point p0, Point p1, Point p2, Point p3) {
Point point;
for(double t = 0.0; t<=1.0; t+=0.001){
point = pow((1-t),3) * p0 + 3 * pow((1-t),2) * t * p1 + 3 * (1-t) * pow(t,2) * p2 + pow(t,3) * p3;
}
}
Can I get this to work somehow using the Point structure instead of QPoint?
If you look at the Qt documentation about QPoint, you'll see that the operator*() and operator+() (that you use here) are overloaded for QPoint.
If you want to make it work with your class, you will need to overload them as well.
The minimum required overloads you need to make your test() function work are:
Point operator*(double factor, const Point & p)
{
return Point{
static_cast<int>(std::round(p.x * factor)),
static_cast<int>(std::round(p.y * factor))
};
}
Point operator+(const Point & p1, const Point & p2)
{
return Point{p1.x + p2.x, p1.y + p2.y};
}
For more consistency, you could add the symetric:
Point operator*(const Point & p, double factor)
{
return factor * p;
}
Anyway, if you want to replace QPoint with Point with full arithmetic compatibility, I would recommend you to write all the overloads (for arithmetic comparison) listed in the documentation of QPoint.
Related
I have a triangle, each point of which is defined by a position (X,Y,Z) and a UV coordinate (U,V):
struct Vertex
{
Vector mPos;
Point mUV;
inline Vector& ToVector() {return mPos;}
inline Vector& ToUV() {return mUV;}
};
With this function, I am able to get the UV coordinate at a specific XYZ position:
Point Math3D::TriangleXYZToUV(Vector thePos, Vertex* theTriangle)
{
Vector aTr1=theTriangle->ToVector()-(theTriangle+1)->ToVector();
Vector aTr2=theTriangle->ToVector()-(theTriangle+2)->ToVector();
Vector aF1 = theTriangle->ToVector()-thePos;
Vector aF2 = (theTriangle+1)->ToVector()-thePos;
Vector aF3 = (theTriangle+2)->ToVector()-thePos;
float aA=aTr1.Cross(aTr2).Length();
float aA1=aF2.Cross(aF3).Length()/aA;
float aA2=aF3.Cross(aF1).Length()/aA;
float aA3=aF1.Cross(aF2).Length()/aA;
Point aUV=(theTriangle->ToUV()*aA1)+((theTriangle+1)->ToUV()*aA2)+((theTriangle+2)->ToUV()*aA3);
return aUV;
}
I attempted to reverse-engineer this to make a function that gets the XYZ coordinate from a specific UV position:
Vector Math3D::TriangleUVToXYZ(Point theUV, Vertex* theTriangle)
{
Point aTr1=theTriangle->ToUV()-(theTriangle+1)->ToUV();
Point aTr2=theTriangle->ToUV()-(theTriangle+2)->ToUV();
Point aF1 = theTriangle->ToUV()-theUV;
Point aF2 = (theTriangle+1)->ToUV()-theUV;
Point aF3 = (theTriangle+2)->ToUV()-theUV;
float aA=gMath.Abs(aTr1.Cross(aTr2)); // NOTE: Point::Cross looks like this: const float Cross(const Point &thePoint) const {return mX*thePoint.mY-mY*thePoint.mX;}
float aA1=aF2.Cross(aF3)/aA;
float aA2=aF3.Cross(aF1)/aA;
float aA3=aF1.Cross(aF2)/aA;
Vector aXYZ=(theTriangle->ToVector()*aA1)+((theTriangle+1)->ToVector()*aA2)+((theTriangle+2)->ToVector()*aA3);
return aXYZ;
}
This works MOST of the time. However, it seems to exponentially "approach" the right-angled corner of the triangle-- or something. I'm not really sure what's going on except that the result gets wildly inaccurate the closer it gets to the right-angle.
What do I need to do to this TriangleUVtoXYZ function to make it return accurate results?
I haven't tested your implementation, but you only need to compute two parametric coordinates - the third being redundant since they should sum to 1.
Vector Math3D::TriangleUVToXYZ(Point theUV, Vertex* theTriangle)
{
// T2-T1, T3-T1, P-T1
Point aTr12 = theTriangle[1].ToUV() - theTriangle[0].ToUV();
Point aTr13 = theTriangle[2].ToUV() - theTriangle[0].ToUV();
Point aP1 = theUV - theTriangle[0].ToUV();
// don't need Abs() for the denominator
float aA23 = aTr12.Cross(aTr13);
// parametric coordinates [s,t]
// s = (P-T1)x(T2-T1) / (T3-T1)x(T2-T1)
// t = (P-T1)x(T3-T1) / (T2-T1)x(T3-T1)
float aA12 = aP1.Cross(aTr12) / -aA23;
float aA13 = aP1.Cross(aTr13) / aA23;
// XYZ = V1 + s(V2-V1) + t(V3-V1)
return theTriangle[0].ToVector()
+ aA12 * (theTriangle[1].ToVector() - theTriangle[0].ToVector())
+ aA13 * (theTriangle[2].ToVector() - theTriangle[0].ToVector());
}
So I'm making a 2D vector class for a class where I create collisions between circular objects with mass and radius on a x-y plane. So everytime a collision happens, I need to update the velocity of the two circles that collided and this relies upon scalar numbers like mass and radius as well as the kinetic energy (scalar) and the momentum (2d vector) of the stones (since momentum and energy conserved , you can solve the momentum and energy of either). All the methods work except for the scalar multiplication. I will only display that method below unless you guys specifically request for me to show the others
Here's my 2d vector class
class vector2d {
public:
double x;
double y;
// Constructor
vector2d() { x=0; y=0; }
vector2d(double_x, double_y) { x=_x; y=_y;}
.
.
.
vector2d operator*(const double& scalar) const {
return {x * scalar, y * scalar };
}
Here's the method in the other class that does the updates the velocity after collision
void collide(Ball *s) {
// Make sure move is called before this to update the position vector'
vec2d diff_pos_s1 = this->init_pos - s->init_pos;
vec2d diff_vel_s1 = this->velocity - s->velocity;
double mass_ratio_s1 = (2 * s->mass) / (this->mass + s->mass);
double num_s1 = diff_pos_s1.dot_product(diff_vel_s1);
double denom_s1 = diff_pos_s1.dot_product(diff_pos_s1);
vec2d v1 = this->velocity - (mass_ratio_s1 * (num_s1 / denom_s1) * diff_pos_s1);
vec2d diff_pos_s2 = s->init_pos - this->init_pos;
vec2d diff_vel_s2 = s->velocity - this->velocity;
double mass_ratio_s2 = (2 * this->mass) / (this->mass + s->mass);
double num_s2 = diff_vel_s2.dot_product(diff_pos_s2);
double denom_s2 = diff_pos_s2.dot_product(diff_pos_s2);
vec2d v2 = s->velocity - (mass_ratio_s2 * (num_s2 / denom_s2) * diff_pos_s2);
this->velocity = v1;
s->velocity = v2;
}
Here's the methods that calculate energy and momentum
double energy() const {
return 0.5 * (mass * velocity * velocity) ;
}
// Calculates the momentum of the balls
vec2d momentum() const {
return mass * velocity;
}
Here are the errors that are produced:
error: no match for 'operator*' (operand types are 'double' and 'vector2d')
error: no match for 'operator*' (operand types are 'const double' and 'vector2d')
Let me know if I should put more information
Your code multiples a double to a vector2d. That won't activate the operator, because the operator will expect a vector2d first. You should have either
vec2d v1 = this->velocity - (diff_pos_s1 * (mass_ratio_s1 * (num_s1 / denom_s1)));
or write an vector2d operator*(double, vector2d), for instance
vector2d operator *(const double & scalar, const vector2d & other) {
return { other.x * scalar, other.y*scalar };
}
As an aside, it seems a waste of time to me to use a reference on a const double.
I'm trying to interpolate a 2D angle and it works 99.9% of the time. For some reason I'm getting -nan(ind) for some values, like:
lastAngle = -0.0613451
currentAngle = -0.061421
alpha = 0.218813
This is the code:
inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
auto v1 = b2Vec2{std::cos(angle1), std::sin(angle1)};
auto v2 = b2Vec2{std::cos(angle2), std::sin(angle2)};
auto v = this->slerp(v1, v2, alpha);
return std::atan2(v.y, v.x);
}
inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
auto cosAngle = v1.x * v2.x + v1.y * v2.y;
auto angle = std::acos(cosAngle);
auto angleAlpha = angle * alpha;
auto v3 = (v2 - (cosAngle * v1)).Normalize();
auto x = v1.x * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
auto y = v1.y * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
return b2Vec2{x, y};
}
All this examples results in inf num:
slerpRotation(-0.0613451f, -0.061421f, 0.218813f);
slerpRotation(-1.63139f, -1.63139f, 0.723703f);
slerpRotation(-0.0614404f, -0.0614034f, 0.199831f);
slerpRotation(0.0194162f, 0.0194164f, 0.259074f);
I've tried to solve this problem for a while now without knowing what causes this problem, do you guys happened to know how to solve this?
In the end you are computing
angle1+alpha*(angle2-angle1)
or if you want to exclude some fringe cases,
angle1+alpha*reduce2pi(angle2-angle1)
where
reduce2pi(phi) = fmod( 3*pi + fmod(phi, 2*pi), 2*pi)-pi
Note that these formulas are completely singularity free, as there is no division. It is not necessary to switch forth and back between angles and their point on the unit circle.
In code, that would be
inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
auto angleDiff = angle2-angle1;
angleDiff = std::fmod(angleDiff, 2*std::M_PI);
angleDiff = std::fmod(angleDiff + 3*std::M_PI, 2*std::M_PI)-std::M_PI;
return angle1+alpha*angleDiff;
}
(12/13/2016) combining several comments: If you insist on using exactly this interface structure, then you can get a singularity free method as follows:
inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
auto angle = std::atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x + v1.y*v2.y);
auto angleAlpha = angle * alpha;
auto v3=b2Vec2{-v1.y, v1.x}; // rotation by 90°
return std::cos(angleAlpha)*v1 + std::sin(angleAlpha)*v3;
}
I'm making a application for school in which I have to click a particular object.
EDIT: This is being made in 2D
I have a rectangle, I rotate this rectangle by X.
The rotation of the rectangle has made my rectangles (x,y,width,height) become a new rectangle around the rotated rectangle.
http://i.stack.imgur.com/MejMA.png
(excuse me for my terrible paint skills)
The Black lines describe the rotated rectangle, the red lines are my new rectangle.
I need to find out if my mouse is within the black rectangle or not. Whatever rotation I do I already have a function for getting the (X,Y) for each corner of the black rectangle.
Now I'm trying to implement this Check if point is within triangle (The same side technique).
So I can either check if my mouse is within each triangle or if theres a way to check if my mouse is in the rotated rectangle that would be even better.
I practically understand everything written in the triangle document, but I simply don't have the math skills to calculate the cross product and the dot product of the 2 cross products.
This is supposed to be the cross product:
a × b = |a| |b| sin(θ) n
|a| is the magnitude (length) of vector a
|b| is the magnitude (length) of vector b
θ is the angle between a and b
n is the unit vector at right angles to both a and b
But how do I calculate the unit vector to both a and b?
And how do I get the magnitude of a vector?
EDIT:
I forgot to ask for the calculation of the dotproduct between 2 cross products.
function SameSide(p1,p2, a,b)
cp1 = CrossProduct(b-a, p1-a)
cp2 = CrossProduct(b-a, p2-a)
if DotProduct(cp1, cp2) >= 0 then return true
else return false
Thank you everyone for your help I think I got the hang of it now, I wish I could accept multiple answers.
If you are having to carry out loads of check, I would shy away from using square root functions: they are computationally expensive. for comparison purposes, just multiply everything by itself and you can bypass the square rooting:
magnitude of vector = length of vector
If vector is defined as float[3] length can be calculated as follows:
double magnitude = sqrt( a[0]*a[0] + a[1]*a[1] + a[2]*a[2] );
However that is expensive computationally so I would use
double magnitudeSquared = a[0]*a[0] + a[1]*a[1] + a[2]*a[2];
Then modify any comparative calculations to use the squared version of the distance or magnitude and it will be more performant.
For the cross product, please forgive me if this maths is shaky, it has been a couple of years since I wrote functions for this (code re-use is great but terrible for remembering things):
double c[3];
c[0] = ( a[1]*b[2] - a[2]*b[1] );
c[1] = ( a[2]*b[0] - a[0]*b[2] );
c[2] = ( a[0]*b[1] - a[1]*b[0] );
To simplify it all I would put a vec3d in a class of its own, with a very simple representation being:
class vec3d
{
public:
float x, y, z;
vec3d crossProduct(vec3d secondVector)
{
vec3d retval;
retval.x = (this.y * secondVector.z)-(secondVector.y * this.z);
retval.y = -(this.x * secondVector.z)+(secondVector.x * this.z);
retval.z = (this.x * secondVector.y)-(this.y * secondVector.x);
return retval;
}
// to get the unit vector divide by a vectors length...
void normalise() // this will make the vector into a 1 unit long variant of itself, or a unit vector
{
if(fabs(x) > 0.0001){
x= x / this.magnitude();
}
if(fabs(y) > 0.0001){
y= y / this.magnitude();
}
if(fabs(z) > 0.0001){
z = / this.magnitude();
}
}
double magnitude()
{
return sqrt((x*x) + (y*y) + (z*z));
}
double magnitudeSquared()
{
return ((x*x) + (y*y) + (z*z));
}
};
A fuller implementation of a vec3d class can be had from one of my old 2nd year coding excercises: .h file and .cpp file.
And here is a minimalist 2d implementation (doing this off the top of my head so forgive the terse code please, and let me know if there are errors):
vec2d.h
#ifndef VEC2D_H
#define VEC2D_H
#include <iostream>
using namespace std;
class Vec2D {
private:
double x, y;
public:
Vec2D(); // default, takes no args
Vec2D(double, double); // user can specify init values
void setX(double);
void setY(double);
double getX() const;
double getY() const;
double getMagnitude() const;
double getMagnitudeSquared() const;
double getMagnitude2() const;
Vec2D normalize() const;
double crossProduct(Vec2D secondVector);
Vec2D crossProduct(Vec2D secondVector);
friend Vec2D operator+(const Vec2D&, const Vec2D&);
friend ostream &operator<<(ostream&, const Vec2D&);
};
double dotProduct(const Vec2D, const Vec2D);
#endif
vec2d.cpp
#include <iostream>
#include <cmath>
using namespace std;
#include "Vec2D.h"
// Constructors
Vec2D::Vec2D() { x = y = 0.0; }
Vec2D::Vec2D(double a, double b) { x = a; y = b; }
// Mutators
void Vec2D::setX(double a) { x = a; }
void Vec2D::setY(double a) { y = a; }
// Accessors
double Vec2D::getX() const { return x; }
double Vec2D::getY() const { return y; }
double Vec2D::getMagnitude() const { return sqrt((x*x) + (y*y)); }
double Vec2D::getMagnitudeSquared() const { return ((x*x) + (y*y)); }
double Vec2D::getMagnitude2 const { return getMagnitudeSquared(); }
double Vec2d::crossProduct(Vec2D secondVector) { return ((this.x * secondVector.getY())-(this.y * secondVector.getX()));}
Vec2D crossProduct(Vec2D secondVector) {return new Vec2D(this.y,-(this.x));}
Vec2D Vec2D::normalize() const { return Vec2D(x/getMagnitude(), y/getMagnitude());}
Vec2D operator+(const Vec2D& a, const Vec2D& b) { return Vec2D(a.x + b.x, a.y + b.y);}
ostream& operator<<(ostream& output, const Vec2D& a) { output << "(" << a.x << ", " << a.y << ")" << endl; return output;}
double dotProduct(const Vec2D a, const Vec2D b) { return a.getX() * b.getX() + a.getY() * b.getY();}
Check if a point is inside a triangle described by three vectors:
float calculateSign(Vec2D v1, Vec2D v2, Vec2D v3)
{
return (v1.getX() - v3.getX()) * (v2.getY() - v3.getY()) - (v2.getX() - v3.getX()) * (v1.getY() - v3.getY());
}
bool isPointInsideTriangle(Vec2D point2d, Vec2D v1, Vec2D v2, Vec2D v3)
{
bool b1, b2, b3;
// the < 0.0f is arbitrary, could have just as easily been > (would have flipped the results but would compare the same)
b1 = calculateSign(point2d, v1, v2) < 0.0f;
b2 = calculateSign(point2d, v2, v3) < 0.0f;
b3 = calculateSign(point2d, v3, v1) < 0.0f;
return ((b1 == b2) && (b2 == b3));
}
In the code above if calculateSign is in the triangle you will get a true returned :)
Hope this helps, let me know if you need more info or a fuller vec3d or 2d class and I can post:)
Addendum
I have added in a small 2d-vector class, to show the differences in the 2d and 3d ones.
The magnitude of a vector is its length. In C++, if you have a vector represented as a double[3], you would calculate the length via
#include <math.h>
double a_length = sqrt( a[0]*a[0] + a[1]*a[1] + a[2]*a[2] );
However, I understand what you actually want is the cross product? In that case, you may want to calculate it directly. The result is a vector, i.e. c = a x b.
You code it like this for example:
double c[3];
c[0] = ( a[2]*b[3] - a[3]*b[2] );
c[1] = ( a[3]*b[1] - a[1]*b[3] );
c[2] = ( a[1]*b[2] - a[2]*b[1] );
You can calculate the magnitude of vector by sqrt(x*x + y*y). Also you can calculate the crossproduct simpler: a x b = a.x * b.y - a.y * b.x. Checking that a point is inside triangle can be done by counting the areas for all 4 triangles. For example a is the area of the source triangle, b,c,d are areas of other ones. If b + c + d = a then the point is inside. Counting the area of triangle is simple: we have vectors a, b that are vertexes of triangle. The area of triangle then is (a x b) / 2
One simple way without getting into vectors is to check for area.
For example ,lets say you have a rectangle with corners A,B,C,D. and point P.
first calculate the area of rectangle, simply find height and width of the rectangle and multiply.
B D
| /
| /
|/____ C
A
For calculating the height,width take one point lets say A, find its distance from all other three points i.e AB,AC,AD 1st and 2nd minimum will be width,and height, max will be diagonal length.
Now store the points from which you get the height, width, lets says those points are B,C.
So now you know how rectangle looks, i.e
B _____ D
| |
|_____|
A C
Then calculate the sum of area of triangles ACP,ABP,BDP,CDP (use heros formula to compute area of rectangle), if it equals to the area of rectangle, point P is inside else outside the rectangle.
We have 3(three) xyz points that define a circle in 3D space, this circle needs to be converted into a polyline(for further rendering). I'm looking for a ready C or C++ function or free library that can do the job.
Don't understand why this was closed. And I can't even answer my own question there. Shame on you guys. But you will not stop the knowledge spreading!
There's a much simpler solution to find the circle parameters in real 3D, just take a look at the "barycentric coordinates" section in http://en.wikipedia.org/wiki/Circumscribed_circle .
You can extract the following optimized code from that:
// triangle "edges"
const Vector3d t = p2-p1;
const Vector3d u = p3-p1;
const Vector3d v = p3-p2;
// triangle normal
const Vector3d w = t.crossProduct(u);
const double wsl = w.getSqrLength();
if (wsl<10e-14) return false; // area of the triangle is too small (you may additionally check the points for colinearity if you are paranoid)
// helpers
const double iwsl2 = 1.0 / (2.0*wsl);
const double tt = t*t;
const double uu = u*u;
// result circle
Vector3d circCenter = p1 + (u*tt*(u*v) - t*uu*(t*v)) * iwsl2;
double circRadius = sqrt(tt * uu * (v*v) * iwsl2*0.5);
Vector3d circAxis = w / sqrt(wsl);
You can then calculate the points on the circle in real 3D too and e.g. draw them using GL_LINE_STRIP in OpenGL. This should be much faster than using the 2D sin/cos approach.
// find orthogonal vector to the circle axis
const Vector3d an = circAxis.getNormalized();
const Vector3d ao = Vector3d(4.0+an[0], 4.0+an[0]+an[1], 4.0+an[0]+an[1]+an[2]).crossProduct(an).getNormalized();
// 4x4 rotation matrix around the circle axis
const int steps = 360; // maybe adjust according to circle size on screen
Matrix4d R = makeRotMatrix4d(circCenter, circAxis, 2.0*M_PI/double(steps));
// one point on the circle
Vector3d cp = circCenter + ao*circRadius;
// rotate point on the circle
for (int i=0; i<steps; ++i)
{
circlePoints.push_back(cp);
cp = transformPoint(cp, R); // apply the matrix
}
For the creation of the transformation matrix (i.e. makeRotMatrix4d()) see http://paulbourke.net/geometry/rotate/ for example.
Please note that I did not test if the above code really compiles, but it should give you enough hints.
There is a nice article and a code sample on how to build a circle by 3 points in 2D, XY plane.
http://paulbourke.net/geometry/circlesphere/
http://paulbourke.net/geometry/circlesphere/Circle.cpp
To build a 3D circle we'll have to:
rotate our 3 points into XY plane
Calculate circle center
build a circle in XY plane using the code in the article
rotate it back into it's original plane
For rotations it is best to use quaternions.
To find a correct quaternion I looked at Ogre3d source code:
void Quaternion::FromAngleAxis (const Radian& rfAngle, const Vector3& rkAxis)
There is one more useful function there:
Quaternion getRotationTo(const Vector3& dest, const Vector3& fallbackAxis = Vector3::ZERO) const
But I didn't use it.
For quaterions and vectors I used our own classes. Here is the full source code of the function that does the job:
bool IsPerpendicular(Point3d *pt1, Point3d *pt2, Point3d *pt3);
double CalcCircleCenter(Point3d *pt1, Point3d *pt2, Point3d *pt3, Point3d *center);
void FindCircleCenter(const Point3d *V1, const Point3d *V2, const Point3d *V3, Point3d *center)
{
Point3d *pt1=new Point3d(*V1);
Point3d *pt2=new Point3d(*V2);
Point3d *pt3=new Point3d(*V3);
if (!IsPerpendicular(pt1, pt2, pt3) ) CalcCircleCenter(pt1, pt2, pt3, center);
else if (!IsPerpendicular(pt1, pt3, pt2) ) CalcCircleCenter(pt1, pt3, pt2, center);
else if (!IsPerpendicular(pt2, pt1, pt3) ) CalcCircleCenter(pt2, pt1, pt3, center);
else if (!IsPerpendicular(pt2, pt3, pt1) ) CalcCircleCenter(pt2, pt3, pt1, center);
else if (!IsPerpendicular(pt3, pt2, pt1) ) CalcCircleCenter(pt3, pt2, pt1, center);
else if (!IsPerpendicular(pt3, pt1, pt2) ) CalcCircleCenter(pt3, pt1, pt2, center);
else {
delete pt1;
delete pt2;
delete pt3;
return;
}
delete pt1;
delete pt2;
delete pt3;
}
bool IsPerpendicular(Point3d *pt1, Point3d *pt2, Point3d *pt3)
// Check the given point are perpendicular to x or y axis
{
double yDelta_a= pt2->y - pt1->y;
double xDelta_a= pt2->x - pt1->x;
double yDelta_b= pt3->y - pt2->y;
double xDelta_b= pt3->x - pt2->x;
// checking whether the line of the two pts are vertical
if (fabs(xDelta_a) <= 0.000000001 && fabs(yDelta_b) <= 0.000000001){
return false;
}
if (fabs(yDelta_a) <= 0.0000001){
return true;
}
else if (fabs(yDelta_b) <= 0.0000001){
return true;
}
else if (fabs(xDelta_a)<= 0.000000001){
return true;
}
else if (fabs(xDelta_b)<= 0.000000001){
return true;
}
else
return false ;
}
double CalcCircleCenter(Point3d *pt1, Point3d *pt2, Point3d *pt3, Point3d *center)
{
double yDelta_a = pt2->y - pt1->y;
double xDelta_a = pt2->x - pt1->x;
double yDelta_b = pt3->y - pt2->y;
double xDelta_b = pt3->x - pt2->x;
if (fabs(xDelta_a) <= 0.000000001 && fabs(yDelta_b) <= 0.000000001){
center->x= 0.5*(pt2->x + pt3->x);
center->y= 0.5*(pt1->y + pt2->y);
center->z= pt1->z;
return 1;
}
// IsPerpendicular() assure that xDelta(s) are not zero
double aSlope=yDelta_a/xDelta_a; //
double bSlope=yDelta_b/xDelta_b;
if (fabs(aSlope-bSlope) <= 0.000000001){ // checking whether the given points are colinear.
return -1;
}
// calc center
center->x= (aSlope*bSlope*(pt1->y - pt3->y) + bSlope*(pt1->x + pt2 ->x)
- aSlope*(pt2->x+pt3->x) )/(2* (bSlope-aSlope) );
center->y = -1*(center->x - (pt1->x+pt2->x)/2)/aSlope + (pt1->y+pt2->y)/2;
return 1;
}
//! Builds a circle in 3D space by 3 points on it and an optional center
void buildCircleBy3Pt(const float *pt1,
const float *pt2,
const float *pt3,
const float *c, // center, can be NULL
std::vector<float> *circle)
{
/* Get the normal vector to the triangle formed by 3 points
Calc a rotation quaternion from that normal to the 0,0,1 axis
Rotate 3 points using quaternion. Points will be in XY plane
Build a circle by 3 points on XY plane
Rotate a circle back into original plane using quaternion
*/
Point3d p1(pt1[0], pt1[1], pt1[2]);
Point3d p2(pt2[0], pt2[1], pt2[2]);
Point3d p3(pt3[0], pt3[1], pt3[2]);
Point3d center;
if (c)
{
center.set(c[0], c[1], c[2]);
}
const Vector3d p2top1 = p1 - p2;
const Vector3d p2top3 = p3 - p2;
const Vector3d circle_normal = p2top1.crossProduct(p2top3).normalize();
const Vector3d xy_normal(0, 0, 1);
Quaternion rot_quat;
// building rotation quaternion
{
// Rotation axis around which we will rotate our circle into XY plane
Vector3d rot_axis = xy_normal.crossProduct(circle_normal).normalize();
const double rot_angle = xy_normal.angleTo(circle_normal); // radians
const double w = cos(rot_angle * 0.5);
rot_axis *= sin(rot_angle * 0.5);
rot_quat.set(w, rot_axis.x, rot_axis.y, rot_axis.z);
}
Quaternion rot_back_quat;
// building backward rotation quaternion, same as prev. but -angle
{
const double rot_angle = -(xy_normal.angleTo(circle_normal)); // radians
const double w_back = cos(rot_angle * 0.5);
Vector3d rot_back_axis = xy_normal.crossProduct(circle_normal).normalize();
rot_back_axis *= sin(rot_angle * 0.5);
rot_back_quat.set(w_back, rot_back_axis.x, rot_back_axis.y, rot_back_axis.z);
}
rot_quat.rotate(p1);
rot_quat.rotate(p2);
rot_quat.rotate(p3);
rot_quat.rotate(center);
if (!c)
{
// calculate 2D center
FindCircleCenter(&p1, &p2, &p3, ¢er);
}
// calc radius
const double radius = center.distanceTo(p1);
const float DEG2RAD = 3.14159f / 180.0f;
// build circle
for (int i = 0; i < 360; ++i)
{
float degInRad = i * DEG2RAD;
Point3d pt(cos(degInRad) * radius + center.x, sin(degInRad) * radius + center.y, 0);
// rotate the point back into original plane
rot_back_quat.rotate(pt);
circle->push_back(pt.x);
circle->push_back(pt.y);
circle->push_back(pt.z);
}
}
The following is the C#/Unity port of Mark's answer. It uses types and utility functions from Unity's scripting API.
// triangle "edges"
var t = p2 - p1;
var u = p3 - p1;
var v = p3 - p2;
// triangle normal
var w = Vector3.Cross(t, u);
var wsl = Vector3.Dot(w, w);
// TODO: if (wsl<10e-14) return false; // area of the triangle is too small (you may additionally check the points for colinearity if you are paranoid)
// helpers
var iwsl2 = 1f / (2f * wsl);
var tt = Vector3.Dot(t, t);
var uu = Vector3.Dot(u, u);
// result circle
Vector3 circCenter = p1 + (u * tt * (Vector3.Dot(u, v)) - t * uu * (Vector3.Dot(t, v))) * iwsl2;
var circRadius = Mathf.Sqrt(tt * uu * (Vector3.Dot(v, v)) * iwsl2 * 0.5f);
Vector3 circAxis = w / Mathf.Sqrt(wsl);
Using Unity's Gizmos, the circle can be drawn as follows (using 30 line segments to approximate it in this case):
// Draw the circle:
Gizmos.color = Color.white;
for (int i = 0; i < 30; ++i)
{
Gizmos.DrawLine(
circCenter + Quaternion.AngleAxis(360f / 30f * i , circAxis) * (p1 - circCenter),
circCenter + Quaternion.AngleAxis(360f / 30f * (i + 1), circAxis) * (p1 - circCenter)
);
}
The result looks like follows for vertex positions var p1 = new Vector3(0f, 1.44f, 0f); var p2 = new Vector3(0f, 0.73f, 0.65f); var p3 = new Vector3(0f, -1.04f, 0f);: