Browse Source

analytical 3D closest arc & multi polylines for one profile

master
gjj 4 months ago
parent
commit
f721b9a95b
  1. 4
      .clang-tidy
  2. 8
      include/common.hpp
  3. 181
      include/line.hpp
  4. 175
      include/solid.hpp
  5. 220
      require.md

4
.clang-tidy

@ -1,9 +1,12 @@
Checks: " Checks: "
-*, -*,
bugprone-*, bugprone-*,
-bugprone-easily-swappable-parameters,
performance-*, performance-*,
readability-*, readability-*,
-readability-braces-around-statements,
misc-*, misc-*,
-misc-non-private-member-variables-in-classes,
clang-analyzer-*, clang-analyzer-*,
clang-diagnostic-*, clang-diagnostic-*,
cppcoreguidelines-*, cppcoreguidelines-*,
@ -16,6 +19,7 @@ Checks: "
-modernize-use-trailing-return-type, -modernize-use-trailing-return-type,
-cppcoreguidelines-magic-numbers, -cppcoreguidelines-magic-numbers,
-cppcoreguidelines-init-variables, -cppcoreguidelines-init-variables,
-cppcoreguidelines-pro-type-member-init,
" "
WarningsAsErrors: '' WarningsAsErrors: ''

8
include/common.hpp

@ -1,8 +1,14 @@
#pragma once #pragma once
#include "real.hpp"
#include <limits>
#include <numbers>
enum PtBoundaryRelation { Inside = -1, OnBoundary, Outside = 1 }; enum PtBoundaryRelation { Inside = -1, OnBoundary, Outside = 1 };
bool isEqual(double a, double b) { const real PI = std::numbers::pi;
const real PI2 = 2 * PI;
const real EPS = std::numeric_limits<real>::epsilon() * 1e2;
inline bool isEqual(double a, double b) {
// TODO // TODO
return a == b; return a == b;
} }

181
include/line.hpp

@ -34,6 +34,7 @@ struct ClosestDescOnSeg {
class ILine { class ILine {
public: public:
int aaa;
virtual ~ILine() = default; virtual ~ILine() = default;
virtual Vec3 eval(real param) = 0; virtual Vec3 eval(real param) = 0;
@ -50,7 +51,7 @@ struct CircularArc {
VecType center; VecType center;
real radius; real radius;
real theta; real theta;
real h; real h = -1; // straight line for h <= 0.
VecType u; VecType u;
VecType v; VecType v;
VecType inCircleDir; VecType inCircleDir;
@ -61,6 +62,12 @@ struct CircularArc {
}; };
; ;
struct AA {
int a;
int b;
void print() const { std::cout << a << " " << b << std::endl; }
};
const real DISC_ARC_ANGLE = std::numbers::pi * 0.125; const real DISC_ARC_ANGLE = std::numbers::pi * 0.125;
class Polyline : public ILine { class Polyline : public ILine {
@ -87,6 +94,10 @@ public:
[[nodiscard]] bool isClosed() const { return _closed; } [[nodiscard]] bool isClosed() const { return _closed; }
[[nodiscard]] const std::vector<CircularArc<Vec3>> &getCircularArcs() const {
return circularArcs;
}
private: private:
Pt3Array _points; Pt3Array _points;
std::vector<real> _bugles; std::vector<real> _bugles;
@ -143,90 +154,128 @@ public:
return -arc.radius * (arc.u * std::cos(phi) + arc.v * std::cos(phi)); return -arc.radius * (arc.u * std::cos(phi) + arc.v * std::cos(phi));
} }
// ClosestDescOnSeg getClosestParam(const Vec3 &p) override {
// real closestDis = std::numeric_limits<real>::max();
// real closestParam;
// for (int i = 0; i < _bugles.size(); ++i) {
// const Vec3 &A = _points[i];
// const Vec3 &B = _points[(i + 1) % _points.size()];
// const auto &arc = circularArcs[i];
// real dis2Seg = segPtDist(p, A, B).dis;
// if (dis2Seg - arc.h > closestDis)
// continue;
// if ((A - p).norm() < closestDis) {
// closestDis = (A - p).norm();
// closestParam = i;
// }
// if ((B - p).norm() < closestDis) {
// closestDis = (B - p).norm();
// closestParam = i + 1;
// }
// int segInsertedCnt = arc.theta / DISC_ARC_ANGLE;
// for (int j = 0; j < segInsertedCnt; ++j) {
// real insertParam = i + j * DISC_ARC_ANGLE / arc.theta;
// const Vec3 insertPt = eval(insertParam);
// real dis2InsertPt = (p - insertPt).norm();
// if (dis2InsertPt < closestDis) {
// closestDis = dis2InsertPt;
// closestParam = insertParam;
// }
// }
// }
// // TODO: 为了鲁棒和精度,应该在每个可能最近的seg上做newton iteration
// int seg = static_cast<int>(closestParam);
// // Q = arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v *
// // std::sin(phi)) d2 = (Q - p)^2
// Vec3 q = eval(closestParam);
// Vec3 qDer1 = der1(closestParam);
// Vec3 qDer2 = der2(closestParam);
// real lDer1 = (q - p).dot(qDer1);
// int iter = 0;
// while (fabs(lDer1) > std::numeric_limits<real>::epsilon() * 1e6) {
// closestParam -= lDer1 / (qDer1.dot(qDer1) + (q - p).dot(qDer2)); // -der1 / der2
// q = eval(closestParam);
// qDer1 = der1(closestParam);
// qDer2 = der2(closestParam);
// lDer1 = (q - p).dot(qDer1);
// printf("After iter %d, dL is %lf\n", iter, lDer1);
// if (closestParam < seg - std::numeric_limits<real>::epsilon()) {
// closestParam = seg;
// closestDis = (_points[seg] - p).norm();
// break;
// }
// if (closestParam > seg + 1 + std::numeric_limits<real>::epsilon()) {
// closestParam = seg + 1;
// closestDis = (_points[(seg + 1) % _points.size()] - p).norm();
// break;
// }
// closestDis = (q - p).norm();
// iter++;
// }
// return {closestParam, closestDis};
// }
ClosestDescOnSeg getClosestParam(const Vec3 &p) override { ClosestDescOnSeg getClosestParam(const Vec3 &p) override {
real closestDis = std::numeric_limits<real>::max(); ClosestDescOnSeg closestDes{};
real closestParam;
for (int i = 0; i < _bugles.size(); ++i) { for (int i = 0; i < _bugles.size(); ++i) {
const Vec3 &A = _points[i]; const Vec3 &a = _points[i];
const Vec3 &B = _points[(i + 1) % _points.size()]; const Vec3 &b = _points[(i + 1) % _points.size()];
const auto &arc = circularArcs[i]; const CircularArc<Vec3> &arc = circularArcs[i];
real dis2Seg = segPtDist(p, A, B).dis; const Vec3 &o = arc.center;
if (dis2Seg - arc.h > closestDis) // p 到圆弧平面的投影
continue; const Vec3 projPt = p - _normal.dot(p - a) * _normal;
if ((A - p).norm() < closestDis) { // projPt到圆的最近点
closestDis = (A - p).norm(); const Vec3 clsPtOnCircle = o + arc.radius * (projPt - o).normalize();
closestParam = i; if ((clsPtOnCircle - a).dot(arc.inCircleDir) > 0) {
} // 在圆弧上
if ((B - p).norm() < closestDis) { real dis = (p - clsPtOnCircle).norm();
closestDis = (B - p).norm(); if (dis < closestDes.dis) {
closestParam = i + 1; closestDes.dis = dis;
Vec3 oa = a - o;
Vec3 oClsPt = clsPtOnCircle - o;
real R2 = arc.radius * arc.radius;
real cosTheta = (oa).dot(oClsPt) / R2;
real theta = std::acos(cosTheta); // [0, pi]
if ((oa.cross(oClsPt)).dot(_normal) < 0) {
theta = 2 * std::numbers::pi - theta;
} }
int segInsertedCnt = arc.theta / DISC_ARC_ANGLE; closestDes.t = i + theta / arc.theta;
for (int j = 0; j < segInsertedCnt; ++j) {
real insertParam = i + j * DISC_ARC_ANGLE / arc.theta;
const Vec3 insertPt = eval(insertParam);
real dis2InsertPt = (p - insertPt).norm();
if (dis2InsertPt < closestDis) {
closestDis = dis2InsertPt;
closestParam = insertParam;
} }
continue;
} }
real paDis = (p - a).norm();
real pbDis = (p - b).norm();
if (paDis < closestDes.dis) {
closestDes.dis = paDis;
closestDes.t = i;
} else {
closestDes.dis = pbDis;
closestDes.t = i + 1;
} }
// TODO: 为了鲁棒和精度,应该在每个可能最近的seg上做newton iteration
int seg = static_cast<int>(closestParam);
// Q = arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v *
// std::sin(phi)) d2 = (Q - p)^2
Vec3 q = eval(closestParam);
Vec3 qDer1 = der1(closestParam);
Vec3 qDer2 = der2(closestParam);
real lDer1 = (q - p).dot(qDer1);
int iter = 0;
while (abs(lDer1) > std::numeric_limits<real>::epsilon() * 1e6) {
closestParam -= lDer1 / (qDer1.dot(qDer1) + (q - p).dot(qDer2)); // -der1 / der2
q = eval(closestParam);
qDer1 = der1(closestParam);
qDer2 = der2(closestParam);
lDer1 = (q - p).dot(qDer1);
printf("After iter %d, dL is %lf\n", iter, lDer1);
if (closestParam < seg - std::numeric_limits<real>::epsilon()) {
closestParam = seg;
closestDis = (_points[seg] - p).norm();
break;
}
if (closestParam > seg + 1 + std::numeric_limits<real>::epsilon()) {
closestParam = seg + 1;
closestDis = (_points[(seg + 1) % _points.size()] - p).norm();
break;
}
closestDis = (q - p).norm();
iter++;
} }
return {closestParam, closestDis}; return closestDes;
} }
const std::vector<CircularArc<Vec3>> &getCircularArcs() const { return circularArcs; }
void print() const { void print() const {
if (_closed) if (_closed)
printf("Closed Polyline: \n"); std::cout << "Closed Polyline: \n";
else else
printf("Open Polyline: \n"); std::cout << "Open Polyline: \n";
printf("Points: {\n"); std::cout << "Points: {\n";
for (int i = 0; i < _points.size(); ++i) { for (int i = 0; i < _points.size(); ++i) {
printf("<%lf, %lf, %lf>", _points[i].x(), _points[i].y(), _points[i].z()); std::cout << _points[i].x() << ", " << _points[i].y() << ", " << _points[i].z() << ">";
if (i != _points.size() - 1) if (i != _points.size() - 1)
printf(", "); std::cout << ", ";
} }
std::cout << "}" << std::endl; std::cout << "}\n";
printf("Bugles: {\n"); std::cout << "的可变参数Bugles: {\n";
for (int i = 0; i < _bugles.size(); ++i) { for (int i = 0; i < _bugles.size(); ++i) {
printf("%lf", _bugles[i]); std::cout << _bugles[i];
if (i != _bugles.size() - 1) if (i != _bugles.size() - 1)
printf(", "); std::cout << ", ";
} }
std::cout << "}" << std::endl; std::cout << "}\n";
} }
static ClosestDescOnSeg segPtDist(const Vec3 &p, const Vec3 &A, const Vec3 &B) { static ClosestDescOnSeg segPtDist(const Vec3 &p, const Vec3 &A, const Vec3 &B) {
Vec3 AB = B - A; Vec3 AB = B - A;
Vec3 AP = p - A; Vec3 AP = p - A;

175
include/solid.hpp

@ -2,7 +2,10 @@
#include "common.hpp" #include "common.hpp"
#include "real.hpp" #include "real.hpp"
#include <cassert>
#include <cstddef>
#include <utility> #include <utility>
#include <vector>
#include "vec.hpp" #include "vec.hpp"
#include "line.hpp" #include "line.hpp"
@ -12,88 +15,110 @@ public:
virtual real sdf(const Vec3 &p) = 0; virtual real sdf(const Vec3 &p) = 0;
}; };
inline Vec2 get2DRepOf3DPt(const Vec3 &pt3D, const Vec3 &u, const Vec3 &v, const Vec3 &localO) {
Vec2 get2DRepOf3DPt(const Vec3 &pt3D, const Vec3 &u, const Vec3 &v, const Vec3 &localO) {
Vec3 OP = pt3D - localO; Vec3 OP = pt3D - localO;
return {OP.dot(u), OP.dot(v)}; return {OP.dot(u), OP.dot(v)};
} }
Vec2 get2DRepOf3DDir(const Vec3 &dir, const Vec3 &u, const Vec3 &v) { inline Vec2 get2DRepOf3DDir(const Vec3 &dir, const Vec3 &u, const Vec3 &v) {
return Vec2{dir.dot(u), dir.dot(v)}.normalize(); return Vec2{dir.dot(u), dir.dot(v)}.normalize();
} }
class IExtrudedSolid : public ISolid { class IExtrudedSolid : public ISolid {
public: public:
Polyline _profile; // TODO: may be replaced by const ref to profile std::vector<Polyline> _profiles; // TODO: may be replaced by const ref to profile
real _rScale; real _rScale;
IExtrudedSolid(std::vector<Polyline> profiles, real rScale)
public: : _profiles(std::move(profiles)), _rScale(rScale) {}
IExtrudedSolid(Polyline profile, real rScale) : _profile(std::move(profile)), _rScale(rScale) {}
}; };
/** /**
* calculate winding number of a point w.r.t. a segment ab * calculate winding number of a point w.r.t. a segment ab
*/ */
real unsignedWindingNumberSegment(const Vec3 &p, const Vec3 &a, const Vec3 &b, // inline real unsignedWindingNumberSegment(const Vec3 &p, const Vec3 &a, const Vec3 &b,
const Vec3 &refNormal) { // const Vec3 &refNormal) {
Vec3 pa = a - p; // Vec3 pa = a - p;
Vec3 pb = b - p; // Vec3 pb = b - p;
return std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.), // return std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.),
static_cast<real>(1.))) // static_cast<real>(1.)))
/ (std::numbers::pi * 2); // / (std::numbers::pi * 2);
} // }
class ExtrudedSolidPolyline : public IExtrudedSolid { class ExtrudedSolidPolyline : public IExtrudedSolid {
private: private:
Polyline _axis; Polyline _axis;
Pt2Array _localProfile2D; std::vector<Pt2Array> _localProfiles2D;
std::vector<CircularArc<Vec2>> _localArcs2d; std::vector<std::vector<CircularArc<Vec2>>> _localArcs2d;
// Pt2Array _localCircleCenter2D; // Pt2Array _localCircleCenter2D;
// Pt2Array _localInCircleDir; // Pt2Array _localInCircleDir;
public: public:
ExtrudedSolidPolyline(Polyline profile, Polyline axis, real rScale) ExtrudedSolidPolyline(std::vector<Polyline> profiles, Polyline axis, real rScale)
: IExtrudedSolid(std::move(profile), rScale), _axis(std::move(axis)) { : IExtrudedSolid(std::move(profiles), rScale), _axis(std::move(axis)) {
assert(!_profiles.empty());
for (const auto &_profile : _profiles) {
assert(_profile.isClosed()); assert(_profile.isClosed());
}
// TODO: project profile at st point to 2D // TODO: project profile at st point to 2D
Vec3 T = _axis.der1(0).normalize(); Vec3 T = _axis.der1(0).normalize();
Vec3 N = _axis.der2(0).normalize(); Vec3 N = _axis.der2(0).normalize();
Vec3 B = T.cross(N); Vec3 B = T.cross(N);
Vec3 Q = _axis.eval(0); Vec3 Q = _axis.eval(0);
int segCount = _profile.getPoints().size(); size_t profileCount = _profiles.size();
_localProfile2D.resize(segCount); _localProfiles2D.resize(profileCount);
_localArcs2d.resize(segCount); _localArcs2d.resize(profileCount);
// _localInCircleDir.resize(segCount); for (int i = 0; i < _profiles.size(); ++i) {
// _localCircleCenter2D.resize(segCount); const auto &profile = _profiles[i];
for (int i = 0; i < segCount; ++i) { size_t segCount = profile.getPoints().size();
_localProfile2D[i] = get2DRepOf3DPt(_profile.getPoints()[i] - Q, N, B, Q); _localProfiles2D[i].resize(segCount);
auto &arc2d = _localArcs2d[i]; _localArcs2d[i].resize(segCount);
const auto &arc3d = _profile.getCircularArcs()[i]; for (int j = 0; j < segCount; ++j) {
// TODO:
_localProfiles2D[i][j] = get2DRepOf3DPt(profile.getPoints()[j] - Q, N, B, Q);
auto &arc2d = _localArcs2d[i][j];
const auto &arc3d = profile.getCircularArcs()[j];
arc2d.center = get2DRepOf3DPt(arc3d.center - Q, N, B, Q); arc2d.center = get2DRepOf3DPt(arc3d.center - Q, N, B, Q);
arc2d.inCircleDir = get2DRepOf3DDir(arc3d.inCircleDir, N, B); arc2d.inCircleDir = get2DRepOf3DDir(arc3d.inCircleDir, N, B);
arc2d.radius = arc3d.radius; arc2d.radius = arc3d.radius;
arc2d.theta = arc3d.theta;
arc2d.h = arc3d.h;
}
} }
} }
real sdf(const Vec3 &p) override { real sdf(const Vec3 &p) override {
ClosestDescOnSeg closestDesc = _axis.getClosestParam(p); ClosestDescOnSeg closestDescToAxis = _axis.getClosestParam(p);
// TNB coordinate system // TNB coordinate system
auto t = closestDesc.t; auto t = closestDescToAxis.t;
Vec3 T = _axis.der1(t).normalize(); Vec3 T = _axis.der1(t).normalize();
Vec3 N = _axis.der2(t).normalize(); Vec3 N = _axis.der2(t).normalize();
Vec3 B = T.cross(N); Vec3 B = T.cross(N);
Vec3 Q = _axis.eval(t); Vec3 Q = _axis.eval(t);
Vec3 QP = p - Q; Vec3 QP = p - Q;
auto p2D = get2DRepOf3DPt(QP, N, B, Q); auto p2D = get2DRepOf3DPt(QP, N, B, Q);
// TODO: to test if p2D is in _localProfile2D // PMC
// for (auto i = 0; i < _localProfile2D.size(); ++i) { PtBoundaryRelation ptProfileRelation = Inside;
// } for (int i = 0; i < _localArcs2d.size(); ++i) {
PtBoundaryRelation ptProfileRelation = getPtProfileRelation(p2D); PtBoundaryRelation relationTmp =
if (ptProfileRelation == OnBoundary) { getPtProfileRelation(p2D, _localProfiles2D[i], _localArcs2d[i]);
if (relationTmp == OnBoundary) {
return 0; // TODO: 判断OnBoundary的过程可以加一点容差 return 0; // TODO: 判断OnBoundary的过程可以加一点容差
} }
ClosestDescOnSeg closestDescOnProfile = distance2Profile2D(p2D); if ((relationTmp == Outside && i == 0) || (relationTmp == Inside && i != 0)) {
return closestDescOnProfile.dis * static_cast<int>(ptProfileRelation); ptProfileRelation = Outside;
break;
}
}
// distance
ClosestDescOnSeg closestDescToProfile{};
for (int i = 0; i < _localArcs2d.size(); ++i) {
ClosestDescOnSeg closestDescTemp =
distance2Profile2D(p2D, _localProfiles2D[i], _localArcs2d[i]);
if (closestDescTemp.dis < closestDescToProfile.dis) {
closestDescToProfile = closestDescTemp;
}
}
return closestDescToProfile.dis * static_cast<int>(ptProfileRelation);
} }
private: private:
@ -103,19 +128,18 @@ private:
* out + in = in * out + in = in
* out + out = out * out + out = out
*/ */
PtBoundaryRelation getPtProfileRelation(const Vec2 &p2D) { static PtBoundaryRelation getPtProfileRelation(const Vec2 &p2D, const Pt2Array &profile2D,
assert(_profile.isClosed()); const std::vector<CircularArc<Vec2>> &arcs2d) {
size_t segCount = arcs2d.size();
int segCount = _profile.getBugles().size();
// 先判断是否在outline上 // 先判断是否在outline上
// 顺便判断点-扇的位置关系 // 顺便判断点-扇的位置关系
bool inFan = false; bool inFan = false;
int onLinesegButHasBugle = -1; int onLinesegButHasBugle = -1;
for (int i = 0; i < segCount; ++i) { for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i]; const Vec2 &a = profile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount]; const Vec2 &b = profile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0) { if (arcs2d[i].h <= EPS) {
//line segment //straight line segment
if (isPointOnSegment(p2D, a, b)) { if (isPointOnSegment(p2D, a, b)) {
return OnBoundary; return OnBoundary;
} }
@ -125,9 +149,9 @@ private:
onLinesegButHasBugle = i; onLinesegButHasBugle = i;
break; break;
} }
const auto &arc = _profile.getCircularArcs()[i]; const auto &arc = arcs2d[i];
real po = (p2D - _localArcs2d[i].center).norm(); real po = (p2D - arc.center).norm();
if ((p2D - a).dot(_localArcs2d[i].inCircleDir) > 0) { if ((p2D - a).dot(arc.inCircleDir) > 0) {
if (po == arc.radius) { if (po == arc.radius) {
return OnBoundary; return OnBoundary;
} }
@ -151,13 +175,13 @@ private:
int majorityIn = 0; // 在多边形内的射线计数 int majorityIn = 0; // 在多边形内的射线计数
int majorityOut = 0; // 在多边形外的射线计数 int majorityOut = 0; // 在多边形外的射线计数
for (int rayIdx = 0; rayIdx < numRays; ++rayIdx) { for (int rayIdx = 0; rayIdx < numRays; ++rayIdx) {
double angle = (2.0 * std::numbers::pi * rayIdx) / numRays; double angle = (PI2 * rayIdx) / numRays;
Vec2 rayDir(cos(angle), sin(angle)); Vec2 rayDir(cos(angle), sin(angle));
int crossings = 0; int crossings = 0;
for (int i = 0; i < segCount; ++i) { for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i]; const Vec2 &a = profile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount]; const Vec2 &b = profile2D[(i + 1) % segCount];
assert(isPointOnSegment(p, a, b)); assert(isPointOnSegment(p, a, b));
// if (isPointOnSegment(p2D, a, b)) // if (isPointOnSegment(p2D, a, b))
// { // {
@ -195,23 +219,20 @@ private:
if (onLinesegButHasBugle != -1) { if (onLinesegButHasBugle != -1) {
// 需要特殊考虑的情况 // 需要特殊考虑的情况
// 从p2D向inCircle方向前进一小步 // 从p2D向inCircle方向前进一小步
Vec2 samplePt = p2D Vec2 samplePt = p2D + arcs2d[onLinesegButHasBugle].inCircleDir * EPS;
+ _localArcs2d[onLinesegButHasBugle].center
* std::numeric_limits<real>::epsilon() * 1e6;
return !ptInPolygon(samplePt) ? Inside : Outside; // 取反 return !ptInPolygon(samplePt) ? Inside : Outside; // 取反
} }
return ptInPolygon(p2D) ^ inFan ? Inside : Outside; return ptInPolygon(p2D) ^ inFan ? Inside : Outside;
// TODO: 返回on的情况
} }
ClosestDescOnSeg distance2Profile2D(const Vec2 &p2D) { static ClosestDescOnSeg distance2Profile2D(const Vec2 &p2D, const Pt2Array &profile2D,
// TODO: 2D 下点到圆弧的距离应该可以直接算,不用这么迭代! const std::vector<CircularArc<Vec2>> &arcs2d) {
assert(_profile.isClosed()); size_t segCount = arcs2d.size();
assert(profile2D.size() == segCount);
ClosestDescOnSeg res{}; ClosestDescOnSeg res{};
for (int i = 0; i < _localArcs2d.size(); ++i) { for (int i = 0; i < segCount; ++i) {
auto disDesc = auto disDesc =
distance2Arc2D(p2D, _localProfile2D[i], distance2Arc2D(p2D, profile2D[i], profile2D[(i + 1) % segCount], arcs2d[i]);
_localProfile2D[(i + 1) % _localArcs2d.size()], _localArcs2d[i]);
if (res.dis > disDesc.dis) { if (res.dis > disDesc.dis) {
res.dis = disDesc.dis; res.dis = disDesc.dis;
res.t = i + disDesc.t; res.t = i + disDesc.t;
@ -220,20 +241,20 @@ private:
return res; return res;
} }
ClosestDescOnSeg distance2Arc2D(const Vec2 &p2D, const Vec2 &a, const Vec2 &b, static ClosestDescOnSeg distance2Arc2D(const Vec2 &p2D, const Vec2 &a, const Vec2 &b,
const CircularArc<Vec2> &arc) { const CircularArc<Vec2> &arc) {
const Vec2 &center = arc.center; const Vec2 &center = arc.center;
Vec2 op = p2D - center; Vec2 op = p2D - center;
Vec2 q = center + arc.radius * op.normalize(); // closest pt on circle Vec2 q = center + arc.radius * op.normalize(); // closest pt on circle
Vec2 oq = q - center;
Vec2 oa = a - center;
// 判断q是否在弧上 // 判断q是否在弧上
if ((q - a).dot(arc.inCircleDir) > 0) { if ((q - a).dot(arc.inCircleDir) > 0) {
// 计算参数 // 计算参数
real normMulti = arc.radius * oq.norm(); Vec2 oq = q - center;
real cosTheta = (oa).dot(oq) / normMulti; Vec2 oa = a - center;
real sinTheta = (oa).cross(oq) / normMulti; real R2 = arc.radius * arc.radius;
return {atan2(sinTheta, cosTheta), (p2D - q).norm()}; real cosTheta = (oa).dot(oq) / R2;
real sinTheta = (oa).cross(oq) / R2;
return {atan2(sinTheta, cosTheta) / arc.theta, (p2D - q).norm()};
} }
real paDis = (a - p2D).norm(); real paDis = (a - p2D).norm();
@ -243,12 +264,13 @@ private:
return {1, pbDis}; return {1, pbDis};
} }
bool isOn2DPolyline(const Vec2 &p2D) { bool isOn2DPolyline(const Vec2 &p2D, const Pt2Array &profile2D,
int segCount = _profile.getBugles().size(); const std::vector<CircularArc<Vec2>> &arcs2d) {
size_t segCount = arcs2d.size();
for (int i = 0; i < segCount; ++i) { for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i]; const Vec2 &a = profile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount]; const Vec2 &b = profile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0) { if (arcs2d[i].h <= EPS) {
//line segment //line segment
if (isPointOnSegment(p2D, a, b)) { if (isPointOnSegment(p2D, a, b)) {
return true; return true;
@ -256,13 +278,16 @@ private:
continue; continue;
} }
} }
// TODO: 没写完,但是暂时用不到
return true;
} }
bool isPointOnSegment(const Vec2 p, const Vec2 &a, const Vec2 &b) { static bool isPointOnSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b) {
// check collinearity // check collinearity
double crossProduct = (p[1] - a[1]) * (b[0] - a[0]) - (p[0] - a[0]) * (b[1] - a[1]); double crossProduct = (p[1] - a[1]) * (b[0] - a[0]) - (p[0] - a[0]) * (b[1] - a[1]);
if (!isEqual(crossProduct, 0)) if (!isEqual(crossProduct, 0)) {
return false; // Not collinear return false; // Not collinear
}
// Check if point is within segment bounds // Check if point is within segment bounds
return (p[0] >= std::min(a[0], b[0]) && p[0] <= std::max(a[0], b[0]) return (p[0] >= std::min(a[0], b[0]) && p[0] <= std::max(a[0], b[0])

220
require.md

@ -78,3 +78,223 @@ virtual CPmSolid * create_face_helix(const PMSoft::CPMGePoint3DArray &pt3d,
double radius, double radius,
double pitch); double pitch);
``` ```
//////////////////////////////////////////////////////////////////////////
/// 体生成
//////////////////////////////////////////////////////////////////////////
// 两截面蒙皮(生成台体)
virtual CPmSolid * create_by_faces_to_skin(PmDbPolyline * pStartPoly, PmDbPolyline * pEndPoly, PMSoft::CPMGeVector3D normal/*,BOOL bIsFace*/) override;
// 多截面蒙皮(截面的边均为线段)
virtual CPmSolid * create_by_faces_to_skin(const CArray<PMSoft::CPMGePoint3DArray, PMSoft::CPMGePoint3DArray&> &jmsPts3d, BOOL bIsFace) override;
// 多截面蒙皮(截面的边可能存在凸度)
virtual CPmSolid * create_by_faces_to_skin(const CArray<PMSoft::CPMGePoint3DArray, PMSoft::CPMGePoint3DArray&> &jmPt3d, const CArray<PmGeDoubleArray, PmGeDoubleArray&> &jmbulge,
CArray<PMSoft::CPMGeVector3D, PMSoft::CPMGeVector3D&> &normalArr, const PMSoft::CPMGePoint3DArray &pathPt3d, BOOL bIsFace) override;
CPmSolid * create_by_faces_to_skin(const CArray<PMSoft::CPMGePoint3DArray, PMSoft::CPMGePoint3DArray&> &jmsPts3d, const CArray<PmGeDoubleArray, PmGeDoubleArray&> &jmsBulges,
const CArray<PMSoft::CPMGeVector3D, PMSoft::CPMGeVector3D&> &normalArr, BOOL bIsFace);
// 多线段蒙皮(线段不含弧)
CPmSolid * createFacesBySkinWithSegs(const CArray<PMSoft::CPMGePoint3DArray, PMSoft::CPMGePoint3DArray&> &segs, BOOL bIsFace);
virtual CPmSolid * createBox(const PMSoft::CPMGePoint3D& p,double xLen,double yLen,double zLen);
virtual CPmSolid * createTorus(const PMSoft::CPMGePoint3D& p, double majorRadius, double minorRadius);
virtual CPmSolid * createSphere(const PMSoft::CPMGePoint3D& p, double radius);
void CreateCircPoints(int nNum, const PMSoft::CPMGePoint3D& pt, const PMSoft::CPMGePoint3D& ptCent, PMSoft::CPMGePoint3DArray& ptCircArr);
CPmSolid * CreateBall(const PMSoft::CPMGePoint3D& ptCent, double dRadius, PMSoft::PMBoolean3d::Ent3dData& drawData);
CPmSolid * CreateBall(const PMSoft::CPMGePoint3D& ptCent, double dRadius);
CPmSolid * CreateBall(const PMSoft::CPMGePoint3D& ptCent, double dRadius, double dHeight);
CPmSolid * CreateBall(const PMSoft::CPMGePoint3D& ptCent, double dRadius, double dHeight, PMSoft::PMBoolean3d::Ent3dData& drawData);
virtual CPmSolid * CreateBallBoard(const PMSoft::CPMGePoint3D& ptCent, double dHeight, double dRadius, double dBoardHeight);
virtual CPmSolid * createPipe(const PMSoft::CPMGePoint3D& axisStart, PMSoft::CPMGePoint3D& axisEnd,
const PMSoft::CPMGeVector3D& baseNormal,
double dblOuterRadius, double dblInnerRadius);
virtual CPmSolid * createCone(const PMSoft::CPMGePoint3D& axisStart, const PMSoft::CPMGePoint3D& axisEnd,
double radius1, double radius2);
virtual CPmSolid * createEllipsoid(const PMSoft::CPMGePoint3D &basept,double height,double radius1,double radius2,double radius3);
virtual CPmSolid * createPyramid(const PMSoft::CPMGePoint3DArray &Pt3d, const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGePoint3D &apex);
virtual CPmSolid * createPyramid(const PMSoft::CPMGePoint3DArray &Pt3d, const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGePoint3DArray &apexs);
virtual CPmSolid * createHelix(const PMSoft::CPMGePoint3DArray &pt3d,
const PmGeDoubleArray bulges,
const PMSoft::CPMGePoint3D &axis_start,
const PMSoft::CPMGePoint3D &axis_end,
const PMSoft::CPMGeVector3D &start_dir,
BOOL handiness,
double radius,
double pitch);
virtual CPmSolid * createHelix(const PMSoft::CPMGePoint3DArray &pt3d,
const PmGeDoubleArray bulges,
const PMSoft::CPMGeVector3D &FaceNormal,
const PMSoft::CPMGePoint3D &axis_start,
const PMSoft::CPMGePoint3D &axis_end,
const PMSoft::CPMGeVector3D &start_dir,
BOOL handiness,
double radius,
double pitch,
BOOL bIsFace = FALSE);
virtual CPmSolid * create_face_helix(const PMSoft::CPMGePoint3DArray &pt3d,
const PmGeDoubleArray bulges,
const PMSoft::CPMGePoint3D &axis_start,
const PMSoft::CPMGePoint3D &axis_end,
const PMSoft::CPMGeVector3D &start_dir,
BOOL handiness,
double radius,
double pitch);
/**
* @brief 截面绕轴旋转生成体
*
*
* @note :
* @param : 截面、基点、轴向量、旋转角度
* @return:
* @author: csl
* @date : [11/7/2022]
*/
virtual CPmSolid * createSolidByFaceAroundAxis(const PMSoft::CPMGePoint3DArray &pt3d, const PmGeDoubleArray &bulges, const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGePoint3D &apex, const PMSoft::CPMGeVector3D &axis,
double angle) override;
virtual CPmSolid * createSphereFace(const PMSoft::CPMGePoint3D& p, double radius);
virtual CPmSolid * create_face_cylinder_cone(const PMSoft::CPMGePoint3D& axisStart, const PMSoft::CPMGePoint3D& axisEnd, double radius1, double radius2, double start = 0, double end = 360);
virtual CPmSolid * extrudeAdvance(PmDbPolyline * pExtrudePoly,
const PMSoft::CPMGePoint3D &fixedPt,
const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGeVector3D &extusionVector,
double scaleFactor,
double twistAngle);
virtual CPmSolid * extrudeAlongPath(const PMSoft::CPMGePoint3DArray &pt3d,
const PmGeDoubleArray &bulge,
PMSoft::CPMGeVector3D normal,
PmDbPolyline * path,
BOOL IsInteset = FALSE) override;
/**
* @brief 带洞截面沿路径拉伸
*
*
* @note :
* @param : 截面必须共面,数组首个元素必须为外包,其余元素为内部洞;拉伸路径可以闭合
* @return:
* @author: csl
* @date : [11/7/2022]
*/
virtual CPmSolid * extrudeAlongPath(const CArray<PmDbPolyline *> & polys,
PmDbPolyline * path) override;
virtual BOOL GetExtents(const CPmSolid *pEntity, PmExtents& extents);
virtual InterferenceType checkInterferenceType(const CPmSolid* Ent,const CPmSolid* otherEnt,double dTol = 0.01);
virtual CPmSolid * extrude(const PMSoft::CPMGePoint3DArray &pt3d, const PmGeDoubleArray &bulge, const PMSoft::CPMGePoint3D &fixedPt, const PMSoft::CPMGeVector3D &plgNormal, const PMSoft::CPMGeVector3D &extusionVector, double scaleFactor, double twistAngle) override;
CPmSolid * extrude(const PMSoft::CPMGePoint3DArray & pt3d, const PmGeDoubleArray & bulge, const PMSoft::CPMGePoint3D &fixedPt, const PMSoft::CPMGeVector3D &extusionVector);
virtual CPmSolid * extrude(PmDbPolyline * pExtrudePoly,
const PMSoft::CPMGePoint3D &fixedPt,
const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGeVector3D &extusionVector,
double scaleFactor,
double twistAngle);
// (新弧形) 面拉伸为柱类体测试接口
PMSoft::PMBoolean3d::CCylinderLikeBody * extrudeCylinderBodyTest(const PMSoft::OdexBase2d::PmDbPLine& plSection, const PMSoft::CPMGeVector3D& vecExtru);
PMSoft::PMBoolean3d::CCylinderLikeBody * extrudeCylinderBodyTest(const PMSoft::CPMGePoint3DArray& pts, const PMSoft::PmGeDoubleArray& bulges, const PMSoft::CPMGeVector3D& vecNormal, const PMSoft::CPMGeVector3D& vecExtru);
/**
* @brief 带洞截面拉伸
*
*
* @note :
* @param : 数组首个元素必须为外包,其余元素为内部洞
* @return:
* @author: csl
* @date : [11/7/2022]
*/
virtual CPmSolid * extrude(const CArray<PmDbPolyline *> & polys,
const PMSoft::CPMGePoint3D &fixedPt,
const PMSoft::CPMGeVector3D &plgNormal,
const PMSoft::CPMGeVector3D &extusionVector,
double scaleFactor = 1.0,
double twistAngle = 0.0) override;
/**
* @brief 多段拉伸生成完整体(适用构造柱马牙槎)
*
*
* @note : 只在自主布尔中实现,ACIS未实现
* @param : polys为各段拉伸的起始截面,vctsExtru为每段拉伸的拉伸向量
* 截面与向量的数量应该相同,且至少为2个
* @return:
* @author: csl
* @date : [9/25/2023]
*/
virtual CPmSolid * extrudeStepByStep(const CArray<PmDbPolyline *> & polys, const CArray<PMSoft::CPMGeVector3D> & vctsExtru) override;
virtual CPmSolid * CreateFaceBody_extrude(const PMSoft::CPMGePoint3DArray & pt3d, const PmGeDoubleArray & bugle, const PMSoft::CPMGeVector3D &plgNormal, double dLength);
virtual CPmSolid * CreateFaceBody_extrude(const PMSoft::CPMGePoint3DArray & pt3d,const PmGeDoubleArray & bugle,const PMSoft::CPMGeVector3D &plgNormal,const PMSoft::CPMGeVector3D &extrudeVec);
virtual CPmSolid * CreateFaceBody_extrude(PmDbPolyline * ply, const PMSoft::CPMGeVector3D &plgNormal, const PMSoft::CPMGeVector3D & extrudeVec);
virtual CPmSolid * CreateFaceBody(const PMSoft::CPMGePoint3DArray & pt3d,const PmGeDoubleArray & bulge,const PMSoft::CPMGeVector3D &plgNormal);
virtual CPmSolid * CreateFaceBody(const PmDbPolyline * pFacePolyLine);
//生成自身带洞的面
virtual CPmSolid * CreateFaceBody(const CArray<PmDbPolyline *> & pFacePolyLineArr) override;
virtual CPmSolid * CreateFaceBody_extrudeAlongPath(const PMSoft::CPMGePoint3DArray & pt3d,const PmGeDoubleArray &bulge,PMSoft::CPMGeVector3D normal,PmDbPolyline* path,BOOL IsInteset = FALSE) override;
virtual void getSliceFace(CPmSolid * &pEntity,const PMSoft::CPMGePoint3D & pt, const PMSoft::CPMGeVector3D & normal, double dTol);
virtual CPmSolid* CreateBodyBySliceFace(CPmSolid * pEntity,const PMSoft::CPMGePoint3D & pt, const PMSoft::CPMGeVector3D & normal, double dExtrudeDis);
virtual BOOL GetEntity_AllFaces(CPmSolid * &pEntity);// 体构件转化为面构件
/// 隐藏不需要的面(适用人防墙业务)
virtual BOOL UnHookSpecialFaces(CPmSolid *& pSolid, CArray<CGeBorder, CGeBorder&> & brds);
virtual BOOL UnHookSpecialFaces(CPmSolid *& pSolid, CArray<CGeBorder, CGeBorder&> & brds, BOOL bNeedTransf);
/// 隐藏单面(适用人防墙业务),向量即为面法向量
virtual BOOL UnHookSingleFace(CPmSolid *& pSolid, const CGeBorder & brd, const PMSoft::CPMGeVector3D & normal);
//////////////////////////////////////////////////////////////////////////
/// 其他
//////////////////////////////////////////////////////////////////////////
virtual BOOL AddEntityToDbs(const CPMSolidArr & EntArr,CPM3dEntDataArr & entDataArr,CString & strACISFilePath);
BOOL RemoveVertexNotNeed(PMSoft::CPMGePoint3DArray & PtArr,PmGeDoubleArray & BulgeArr,int & iSize_Pt,int & iSize_Bulge);
/***************************************************************************************************************************************************/
virtual BOOL getCentroid(CPmSolid *pEntity, PMSoft::CPMGePoint3D &centroid);
//describe:获取所需实体的所有符合条件面,判断条件为实体面的法向量和传入向量符合条件
//notice:bGetOrMove,获取符合条件的还是去除符合条件的;bStrict,绝对符合条件还是接近条件(e.g 绝对条件为(线/面)同向时,接近条件为锐角;绝对条件为noZ时,接近条件为非xy平面)
//return:
//
//author:csl
//date:2022/9/14
virtual BOOL GetFaceSuitVec(CPmSolid *& pSolid, FaceNormalType normalType, BOOL bGetOrMove, BOOL bStrict, const PMSoft::CPMGeVector3D & vctNormalStd, const PMSoft::CPMGeTol& tol = PMSoft::CPMGeTol::gTol) override;
// 一组实体 向 指定面进行投影
virtual CPmSolid * CreateEntShadow(const CPMSolidArr & entArr, const PMSoft::CPMGePlane & plane) override;
// 将面实体拆分,每个面作为一个Solid,放在结果数组中。!内存需要调用方管理
virtual BOOL SplitFacesSolid(const CPmSolid * pFacesSolid, CPMSolidArr & facesSplitResult) override;
// 将面Solid转化为体Solid(原始面组必须能构成单个完整体,不能多面,也不能缺面)
virtual BOOL createBodyByFaces(CPmSolid *& pSolid) override;
Loading…
Cancel
Save