|
|
@ -2,6 +2,7 @@ |
|
|
|
|
|
|
|
#include "common.hpp" |
|
|
|
#include "real.hpp" |
|
|
|
#include <array> |
|
|
|
#include <cassert> |
|
|
|
#include <cstddef> |
|
|
|
#include <utility> |
|
|
@ -22,12 +23,18 @@ inline Vec2 get2DRepOf3DPt(const Vec3 &pt3D, const Vec3 &u, const Vec3 &v, const |
|
|
|
inline Vec2 get2DRepOf3DDir(const Vec3 &dir, const Vec3 &u, const Vec3 &v) { |
|
|
|
return Vec2{dir.dot(u), dir.dot(v)}.normalize(); |
|
|
|
} |
|
|
|
class IExtrudedSolid : public ISolid { |
|
|
|
public: |
|
|
|
|
|
|
|
template <typename AxisLineType> |
|
|
|
class IExtrudedSolidBase : public ISolid { |
|
|
|
protected: |
|
|
|
std::vector<Polyline> _profiles; // TODO: may be replaced by const ref to profile
|
|
|
|
AxisLineType _axis; |
|
|
|
real _rScale; |
|
|
|
IExtrudedSolid(std::vector<Polyline> profiles, real rScale) |
|
|
|
: _profiles(std::move(profiles)), _rScale(rScale) {} |
|
|
|
IExtrudedSolidBase(std::vector<Polyline> profiles, AxisLineType axis, real rScale) |
|
|
|
: _profiles(std::move(profiles)), _axis(std::move(axis)), _rScale(rScale) {} |
|
|
|
|
|
|
|
public: |
|
|
|
virtual real sdf(const Vec3 &p) = 0; |
|
|
|
}; |
|
|
|
|
|
|
|
/**
|
|
|
@ -42,25 +49,26 @@ public: |
|
|
|
// / (std::numbers::pi * 2);
|
|
|
|
// }
|
|
|
|
|
|
|
|
class ExtrudedSolidPolyline : public IExtrudedSolid { |
|
|
|
// template <typename AxisLineType>
|
|
|
|
class ExtrudedSolidPolyline : public IExtrudedSolidBase<Polyline> { |
|
|
|
private: |
|
|
|
Polyline _axis; |
|
|
|
std::vector<Pt2Array> _localProfiles2D; |
|
|
|
std::vector<std::vector<CircularArc<Vec2>>> _localArcs2d; |
|
|
|
// Pt2Array _localCircleCenter2D;
|
|
|
|
// Pt2Array _localInCircleDir;
|
|
|
|
Vec3 _biNormal; |
|
|
|
|
|
|
|
public: |
|
|
|
ExtrudedSolidPolyline(std::vector<Polyline> profiles, Polyline axis, real rScale) |
|
|
|
: IExtrudedSolid(std::move(profiles), rScale), _axis(std::move(axis)) { |
|
|
|
: IExtrudedSolidBase(std::move(profiles), std::move(axis), rScale) { |
|
|
|
assert(!_profiles.empty()); |
|
|
|
for (const auto &_profile : _profiles) { |
|
|
|
assert(_profile.isClosed()); |
|
|
|
} |
|
|
|
// TODO: project profile at st point to 2D
|
|
|
|
Vec3 T = _axis.der1(0).normalize(); |
|
|
|
Vec3 N = _axis.der2(0).normalize(); |
|
|
|
Vec3 B = T.cross(N); |
|
|
|
Vec3 Q = _axis.eval(0); |
|
|
|
// project profile at st point to 2D
|
|
|
|
Vec3 tangent = _axis.der1(0).normalize(); |
|
|
|
Vec3 normal = _axis.der2(0).normalize(); |
|
|
|
_biNormal = tangent.cross(normal); |
|
|
|
assert(_biNormal.isParallel(_axis.getNormal())); |
|
|
|
Vec3 q = _axis.eval(0); |
|
|
|
size_t profileCount = _profiles.size(); |
|
|
|
_localProfiles2D.resize(profileCount); |
|
|
|
_localArcs2d.resize(profileCount); |
|
|
@ -71,11 +79,12 @@ public: |
|
|
|
_localArcs2d[i].resize(segCount); |
|
|
|
for (int j = 0; j < segCount; ++j) { |
|
|
|
// TODO:
|
|
|
|
_localProfiles2D[i][j] = get2DRepOf3DPt(profile.getPoints()[j] - Q, N, B, Q); |
|
|
|
_localProfiles2D[i][j] = |
|
|
|
get2DRepOf3DPt(profile.getPoints()[j] - q, normal, _biNormal, q); |
|
|
|
auto &arc2d = _localArcs2d[i][j]; |
|
|
|
const auto &arc3d = profile.getCircularArcs()[j]; |
|
|
|
arc2d.center = get2DRepOf3DPt(arc3d.center - Q, N, B, Q); |
|
|
|
arc2d.inCircleDir = get2DRepOf3DDir(arc3d.inCircleDir, N, B); |
|
|
|
arc2d.center = get2DRepOf3DPt(arc3d.center - q, normal, _biNormal, q); |
|
|
|
arc2d.inCircleDir = get2DRepOf3DDir(arc3d.inCircleDir, normal, _biNormal); |
|
|
|
arc2d.radius = arc3d.radius; |
|
|
|
arc2d.theta = arc3d.theta; |
|
|
|
arc2d.h = arc3d.h; |
|
|
@ -87,52 +96,81 @@ public: |
|
|
|
ClosestDescOnSeg closestDescToAxis = _axis.getClosestParam(p); |
|
|
|
// TNB coordinate system
|
|
|
|
auto t = closestDescToAxis.t; |
|
|
|
Vec3 Q = _axis.eval(t); // closest point on axis
|
|
|
|
Vec3 QP = p - Q; |
|
|
|
Vec3 T = _axis.der1(t).normalize(); |
|
|
|
if (_axis.isEndParm(t) && fabs(QP.dot(T)) > EPS) { |
|
|
|
Vec3 q = _axis.eval(t); // closest point on axis
|
|
|
|
Vec3 qp = p - q; |
|
|
|
auto TBN = getTBN(p, q, closestDescToAxis); |
|
|
|
const Vec3 &tangent = TBN[0]; |
|
|
|
const Vec3 &normal = TBN[1]; |
|
|
|
const Vec3 &biNormal = TBN[2]; |
|
|
|
if (_axis.isEndParam(t) && fabs(qp.dot(tangent)) > EPS) { |
|
|
|
// project p to the plane passing through Q and perpendicular to T
|
|
|
|
Vec3 projP = p + T * (-QP.dot(T)); |
|
|
|
real pqDotT = -qp.dot(tangent); |
|
|
|
Vec3 projP = p + tangent * pqDotT; |
|
|
|
Vec2 p2D = get2DRepOf3DPt(projP, normal, biNormal, q); |
|
|
|
PtBoundaryRelation ptProfileRelation = pmcProfile2d(p2D); |
|
|
|
real projectedDis = 0; |
|
|
|
if (ptProfileRelation == Outside) |
|
|
|
projectedDis = disProfile2D(p2D).dis; |
|
|
|
// must be positive (outside)
|
|
|
|
return sqrt(projectedDis * projectedDis + pqDotT * pqDotT); |
|
|
|
} |
|
|
|
|
|
|
|
Vec3 N = _axis.der2(t).normalize(); |
|
|
|
Vec3 B = T.cross(N); |
|
|
|
Vec2 p2D = get2DRepOf3DPt(p, normal, biNormal, q); |
|
|
|
PtBoundaryRelation ptProfileRelation = pmcProfile2d(p2D); |
|
|
|
if (ptProfileRelation == OnBoundary) |
|
|
|
return 0; |
|
|
|
auto closestDescToProfile = disProfile2D(p2D); |
|
|
|
return closestDescToProfile.dis * static_cast<int>(ptProfileRelation); |
|
|
|
} |
|
|
|
|
|
|
|
auto p2D = get2DRepOf3DPt(QP, N, B, Q); |
|
|
|
// PMC
|
|
|
|
PtBoundaryRelation ptProfileRelation = Inside; |
|
|
|
for (int i = 0; i < _localArcs2d.size(); ++i) { |
|
|
|
PtBoundaryRelation relationTmp = |
|
|
|
getPtProfileRelation(p2D, _localProfiles2D[i], _localArcs2d[i]); |
|
|
|
if (relationTmp == OnBoundary) { |
|
|
|
return 0; // TODO: 判断OnBoundary的过程可以加一点容差
|
|
|
|
std::array<Vec3, 3> getTBN(const Vec3 &p, const Vec3 &q, const ClosestDescOnSeg &closestDesc) { |
|
|
|
real t = closestDesc.t; |
|
|
|
if (std::abs(t - std::round(t)) < EPS) { |
|
|
|
// 端点处
|
|
|
|
// p到圆弧平面的投影
|
|
|
|
Vec3 projPt = p - _biNormal.dot(p - q) * _biNormal; |
|
|
|
Vec3 normal = (q - projPt).normalize(); |
|
|
|
if (normal.dot(_axis.der2(t)) < 0) { |
|
|
|
normal = -normal; |
|
|
|
} |
|
|
|
if ((relationTmp == Outside && i == 0) || (relationTmp == Inside && i != 0)) { |
|
|
|
ptProfileRelation = Outside; |
|
|
|
break; |
|
|
|
return {normal.cross(_biNormal), normal, _biNormal}; |
|
|
|
} |
|
|
|
return {_axis.der1(t).normalize(), _axis.der1(t).normalize(), _biNormal}; |
|
|
|
} |
|
|
|
|
|
|
|
// distance
|
|
|
|
private: |
|
|
|
inline ClosestDescOnSeg disProfile2D(const Vec2 &p2D) { |
|
|
|
ClosestDescOnSeg closestDescToProfile{}; |
|
|
|
for (int i = 0; i < _localArcs2d.size(); ++i) { |
|
|
|
ClosestDescOnSeg closestDescTemp = |
|
|
|
distance2Profile2D(p2D, _localProfiles2D[i], _localArcs2d[i]); |
|
|
|
ClosestDescOnSeg closestDescTemp = disLoop2D(p2D, _localProfiles2D[i], _localArcs2d[i]); |
|
|
|
if (closestDescTemp.dis < closestDescToProfile.dis) { |
|
|
|
closestDescToProfile = closestDescTemp; |
|
|
|
} |
|
|
|
} |
|
|
|
return closestDescToProfile.dis * static_cast<int>(ptProfileRelation); |
|
|
|
return closestDescToProfile; |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
/** 低于2Dprofile的内外部判定
|
|
|
|
inline PtBoundaryRelation pmcProfile2d(const Vec2 &p2D) { |
|
|
|
// PMC
|
|
|
|
for (int i = 0; i < _localArcs2d.size(); ++i) { |
|
|
|
PtBoundaryRelation relationTmp = pmcLoop2d(p2D, _localProfiles2D[i], _localArcs2d[i]); |
|
|
|
if (relationTmp == OnBoundary) { |
|
|
|
return OnBoundary; // TODO: 判断OnBoundary的过程可以加一点容差
|
|
|
|
} |
|
|
|
if ((relationTmp == Outside && i == 0) || (relationTmp == Inside && i != 0)) { |
|
|
|
return Outside; |
|
|
|
} |
|
|
|
} |
|
|
|
return Inside; |
|
|
|
} |
|
|
|
|
|
|
|
/** 2D Loop的内外部判定
|
|
|
|
* in + in = out |
|
|
|
* in + out = in |
|
|
|
* out + in = in |
|
|
|
* out + out = out |
|
|
|
*/ |
|
|
|
static PtBoundaryRelation getPtProfileRelation(const Vec2 &p2D, const Pt2Array &profile2D, |
|
|
|
static PtBoundaryRelation pmcLoop2d(const Vec2 &p2D, const Pt2Array &loop2D, |
|
|
|
const std::vector<CircularArc<Vec2>> &arcs2d) { |
|
|
|
size_t segCount = arcs2d.size(); |
|
|
|
// 先判断是否在outline上
|
|
|
@ -140,8 +178,8 @@ private: |
|
|
|
bool inFan = false; |
|
|
|
int onLinesegButHasBugle = -1; |
|
|
|
for (int i = 0; i < segCount; ++i) { |
|
|
|
const Vec2 &a = profile2D[i]; |
|
|
|
const Vec2 &b = profile2D[(i + 1) % segCount]; |
|
|
|
const Vec2 &a = loop2D[i]; |
|
|
|
const Vec2 &b = loop2D[(i + 1) % segCount]; |
|
|
|
if (arcs2d[i].h <= EPS) { |
|
|
|
//straight line segment
|
|
|
|
if (isPointOnSegment(p2D, a, b)) { |
|
|
@ -184,8 +222,8 @@ private: |
|
|
|
int crossings = 0; |
|
|
|
|
|
|
|
for (int i = 0; i < segCount; ++i) { |
|
|
|
const Vec2 &a = profile2D[i]; |
|
|
|
const Vec2 &b = profile2D[(i + 1) % segCount]; |
|
|
|
const Vec2 &a = loop2D[i]; |
|
|
|
const Vec2 &b = loop2D[(i + 1) % segCount]; |
|
|
|
assert(isPointOnSegment(p, a, b)); |
|
|
|
// if (isPointOnSegment(p2D, a, b))
|
|
|
|
// {
|
|
|
@ -229,14 +267,13 @@ private: |
|
|
|
return ptInPolygon(p2D) ^ inFan ? Inside : Outside; |
|
|
|
} |
|
|
|
|
|
|
|
static ClosestDescOnSeg distance2Profile2D(const Vec2 &p2D, const Pt2Array &profile2D, |
|
|
|
static ClosestDescOnSeg disLoop2D(const Vec2 &p2D, const Pt2Array &loop2D, |
|
|
|
const std::vector<CircularArc<Vec2>> &arcs2d) { |
|
|
|
size_t segCount = arcs2d.size(); |
|
|
|
assert(profile2D.size() == segCount); |
|
|
|
assert(loop2D.size() == segCount); |
|
|
|
ClosestDescOnSeg res{}; |
|
|
|
for (int i = 0; i < segCount; ++i) { |
|
|
|
auto disDesc = |
|
|
|
distance2Arc2D(p2D, profile2D[i], profile2D[(i + 1) % segCount], arcs2d[i]); |
|
|
|
auto disDesc = distance2Arc2D(p2D, loop2D[i], loop2D[(i + 1) % segCount], arcs2d[i]); |
|
|
|
if (res.dis > disDesc.dis) { |
|
|
|
res.dis = disDesc.dis; |
|
|
|
res.t = i + disDesc.t; |
|
|
@ -330,8 +367,3 @@ private: |
|
|
|
// return arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v * std::sin(phi));
|
|
|
|
// }
|
|
|
|
}; |
|
|
|
|
|
|
|
class ExtrudedSolidPolynomialLine : public IExtrudedSolid { |
|
|
|
protected: |
|
|
|
PolynomialLine _axis; |
|
|
|
}; |
|
|
|