Build Circle from 3 Points in 3D space implementation in C or C++

匿名 (未验证) 提交于 2019-12-03 02:52:02

问题:

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!

回答1:

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.



回答2:

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, &center);     }      // 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);     } } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!