Browse Source

contianment and distance, with some TODOs

master
Dtouch 7 months ago
parent
commit
7423f4d892
  1. 409
      .clang-format
  2. 23
      .clang-tidy
  3. 137
      include/line.hpp
  4. 339
      include/solid.hpp

409
.clang-format

@ -0,0 +1,409 @@
---
Language: Cpp
# 基于的编码规范, 可选:
# - LLVM: https://llvm.org/docs/CodingStandards.html
# - Google: https://google.github.io/styleguide/cppguide.html
# - Chromium: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/styleguide/styleguide.md
# - Mozilla: https://firefox-source-docs.mozilla.org/code-quality/coding-style/index.html
# - WebKit: https://www.webkit.org/coding/coding-style.html
# - Microsoft: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
# - GNU: https://www.gnu.org/prep/standards/standards.html
# - InheritParentConfig: 继承父目录的编码规范, 如果有的话, 不是一个真正的编码规范
# - None: 不使用, 即自动配置, 也就是本文件中的自定义内容
# BasedOnStyle: LLVM
#访问声明符缩进
AccessModifierOffset: -4
# 开括号后的对齐(包括小括号/大括号/尖括号), 建议使用Align
# - Align: 对于开括号, 即在换行情况下, 换行的参数跟开括号对齐, 建议使用
# - DontAlign: 不对于开括号, 即换行时使用配置的空格数
# - AlwaysBreak: 永远换行, 即第一个参数都不允许粘连括号, 会强制换行, 换行后使用配置空格数对齐
# - BlockIndent: 同AlwaysBreak, 多了一个操作: 如果参数不固定在同一行, 闭括号将在下一行
AlignAfterOpenBracket: Align
# - 结构休数组统一初始化对齐, 建议不配置, 没过多必要, 详见clang-format doc
# - None: 不做处理, 即保留开发者的代码
# - Left: 左对齐
# - Right: 右对齐
AlignArrayOfStructures: None
# 连续赋值语句的对齐,即多个赋值语句连续出现时的对齐策略配置, 包含多个子配置项
AlignConsecutiveAssignments:
# 是否启用, 建议不启用
Enabled: false
# 是否跨过空行, 即多个对齐语句中间有空行时, 是否跨过, 如果要开启连续赋值语句的配置, 建议为false
AcrossEmptyLines: false
# 同AcrossComments: 即是否跨过注释, 建议false
AcrossComments: false
# 是否跨过复合语句(包括空行及注释), 建议False
AlignCompound: false
# 是否(右)对齐赋值操作的操作符, 建议true
PadOperators: true
# 同AlignConsecutiveAssignments, 表示连续位定义语句出现时, 是否需要对齐:符号, 位变量定义用得少, 可以不开启
AlignConsecutiveBitFields:
# 是否启用, 建议不启用
Enabled: false
# 同AlignConsecutiveAssignments
AcrossEmptyLines: false
# 同AlignConsecutiveAssignments
AcrossComments: false
# 只在AlignConsecutiveAssignments配置中有效, 自动生成的clang-format有此项, 忽略
AlignCompound: false
# 只在AlignConsecutiveAssignments配置中有效, 自动生成的clang-format有此项, 忽略
PadOperators: false
# 是否对齐连续声明, 同AlignConsecutiveDeclarations
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
# 只在AlignConsecutiveAssignments配置中有效, 自动生成的clang-format有此项, 忽略
AlignCompound: false
# 只在AlignConsecutiveAssignments配置中有效, 自动生成的clang-format有此项, 忽略
PadOperators: false
# 续行符(\\)对齐:
# - DontAlign: 不做操作
# - Left: 尽可能向左对齐, 即最长一行代码为准
# - Right: 跟开发都写的最远的\\对齐(即不会自动缩减你的空格), 建议使用这个
AlignEscapedNewlines: Right
# 在二元/一元表达式中的操作数对齐, 可选值:
# - DontAlign: 不做对齐, 在操作数换行后, 将使用ContinuationIndentWidth来对齐
# - Align: 即换行时, 操作数(or 操作符加操作数)跟上一行的第一个操作数左对齐, 具体操作符要不要换行, 由BreakBeforeBinaryOperators配置决定
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
# 允许短的函数放在同一行, 可选值: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
AllowShortFunctionsOnASingleLine: All
# 允许lambda在一行中, 同上, 建议All
AllowShortLambdasOnASingleLine: All
# 是否将简单的if(else/else if)语句中的body跟if(else/else if)放置于同一行,可选值
# - Never: 永远不, 建议值
# - WithoutElse: 没有else/else if时, 允许
# - OnlyFirstIf: 只有第一个if允许
# - AllIfAndElse: 所有的if/else都允许
AllowShortIfStatementsOnASingleLine: Never
# 是否允许loop语句体跟loop语句共行, true/false, 建议false
AllowShortLoopsOnASingleLine: false
# Deprecated, 废弃定义, 设置为None即可
AlwaysBreakAfterDefinitionReturnType: None
# Return类型后是否换行, 诡异的定义, 请设置为None即可
AlwaysBreakAfterReturnType: None
# 多常量字符串定义是, 是否在第一个字符串常量前换行, true/false, 建议false
AlwaysBreakBeforeMultilineStrings: false
# 模板声明换行风格, 可选值:
# - No: 永远不对开发者的风格作处理
# - MultiLine: 建议值, 即仅在开发者写的模板声明(包括函数)跨越多行时, 进行换行, 否则维持原样
# - Yes: 不管如何都进行分行, 不建议
AlwaysBreakTemplateDeclarations: MultiLine
# 属性宏列表, 自定义, 用于语言扩展或静态分析注解, 可忽略
AttributeMacros:
- __capability
# 函数调用时的参数(Arguments)是否放置于一行, false不放置, true强制一个调用参数一行, 建议false
BinPackArguments: false
# 函数定义参数(Parameters)是否放置于一行, 同BinPackArguments
BinPackParameters: false
# 大括号换行
BraceWrapping:
# 在case后的大括号是否换行
AfterCaseLabel: true
# class后
AfterClass: true
# 控制语句(if/for/while/switch/...)后是否换行
# - Never: 永远不, 即永远将语句体的大括号放置于控制语句同一行
# - MultiLine: 多行控制语句才进行换行
# - Always: 永远换行, 建议
AfterControlStatement: Always
# 下面比较容易理解, 不再作无意义的解释
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
# 二元操作符前是否换行, 建议为None
BreakBeforeBinaryOperators: None
# 概念声明前是否换行, 建议Always
BreakBeforeConceptDeclarations: Always
# 大括号换行风格,Custom即可, 具体值可参考上方文档
BreakBeforeBraces: Custom
# 继承列表括号前换行, false即可
BreakBeforeInheritanceComma: false
# 是否将整个继承列表换行
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
# 是否在构造函数初始化列表的,前换行
BreakConstructorInitializersBeforeComma: false
# 继承列表换行风格, 使用BeforeComma适合
BreakConstructorInitializers: BeforeComma
# Java注解相关, 跳过
BreakAfterJavaFieldAnnotations: false
# 字面字符串是否换行, true
BreakStringLiterals: true
# 代码列字符上限
ColumnLimit: 120
# pragma注释
CommentPragmas: '^ IWYU pragma:'
# 注释关键字对齐(const/volatile), 建议Leave
# - Leave: - 不改变开发者定义
# - Left: 位于类型前
# - Right: 位于类型后
# - Custom: 自定义
QualifierAlignment: Leave
# 未在文档中找到
CompactNamespaces: false
# 构造函数初始化列表缩进, 建议0
ConstructorInitializerIndentWidth: 0
# 函数调用续行对齐, 建议4
ContinuationIndentWidth: 4
# C++11的统一初始化列表大括号风格, 建议true
Cpp11BracedListStyle: true
# 提取行结束符并标准化, 建议false, 不要进行分析及自动运用, 而是强制使用UseCRLF设定来做
DeriveLineEnding: true
# 是否开启文件分析, 根据文件中的*/&使用情况更新clang-format设定, 在无法决定时, 使用PointerAlignment代替, 不建议开启
DerivePointerAlignment: false
DisableFormat: false
# 访问限定后是否添加空行, 建议Never
EmptyLineAfterAccessModifier: Never
# 访问限定前是否要求空行, 建议LogicalBlock
EmptyLineBeforeAccessModifier: LogicalBlock
# 实验性的自动检测同行并进行操作, 建议false
ExperimentalAutoDetectBinPacking: false
# 是否打包构造函数初始化列表, 建议Never, 可选:
# - Never: 永远不做操作, 即一个参数一行
# - BinPack: 两个参数一行
# - CurrentLine: 所有参数放置于一行, 如果放不下, 就一个参数一行
# - NextLine: 同CurrentLine有点像, 唯一不同就是如果放不行, 将剩余参数放置于下一行(即不自动一参一行)
PackConstructorInitializers: Never
BasedOnStyle: ''
# 废弃配置
ConstructorInitializerAllOnOneLineOrOnePerLine: false
# 废弃配置
AllowAllConstructorInitializersOnNextLine: true
# 是否强制在namespace结尾增加 // namespace xxx, 建议为true
FixNamespaceComments: true
# 大于多少行namespace内的代码行时才在namespace结尾添加 // namespace xxx, 建议0,即无论如何都添加
ShortNamespaceLines: 0
# Macro宏
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
#If宏
IfMacros:
- KJ_IF_MAYBE
# include代码块操作, 前提是SortIncludes开启:
# - Preserve: 只对每个代码块排序
# - Merge: 对所有代码块合并, 并在合并后排序
# - Regroup: 对所有include块进行分析, 并重新分块, 不建议!
IncludeBlocks: Preserve
# Include Sort选项, 可选:
# - Never: 永远不, 建议
# - CaseSensitive: 大小写敏感排序
# - CaseInsensitive: 大小写不敏感排序
SortIncludes: Never
# Include种类, 默认即可
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
# 缩进访问控制
IndentAccessModifiers: false
# 缩进case语句, 建议false
IndentCaseLabels: false
# 缩进case body, 建议true
IndentCaseBlocks: true
# 缩进goto标签
IndentGotoLabels: true
# 预处理指示(PPD-PreProcessor Directive)缩进, 建议None
# - None: 不缩进
# - AfterHash: #不缩进, #后面的指示缩进
# - BeforeHash: #跟前缩进
IndentPPDirectives: None
# extern "C"缩进, 建议AfterExternBlock
IndentExternBlock: AfterExternBlock
# 模板require是否缩进
IndentRequiresClause: true
# 缩进宽度
IndentWidth: 4
# 函数名换行时, 是否缩进(即返回值跟名字不同行时), 建议false
IndentWrappedFunctionNames: false
# 是否在代码块中(if/else/for/do/while)强制插入大括号, 建议false
InsertBraces: false
# 是否强制插入拖尾的',', 建议为None
InsertTrailingCommas: None
# Java相关, 跳过
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
# 是否block开始前有一个empty line, 诡异, 直接false
KeepEmptyLinesAtTheStartOfBlocks: false
# 未找到定义
LambdaBodyIndentation: Signature
# 宏开始的正则, 不使用
MacroBlockBegin: ''
# 宏结束的正则, 不使用
MacroBlockEnd: ''
# 空行保持, 建议为1
MaxEmptyLinesToKeep: 1
# Namespace内的对齐, 直接使用None即可, 即所有namespace内(包括内嵌的)都不indent
NamespaceIndentation: None
# Obj-C语言设置, 跳过
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
# 罚分设定(根据你的"违规"值选择罚分少的)
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
# 指针对齐, 建议Right
PointerAlignment: Right
# 引用对齐, 可选:
# - Pointer: 使用'PointerAlignment'配置, 建议使用
# - Left: Left
# - Right: Right
ReferenceAlignment: Pointer
# 预处理对齐宽度
PPIndentWidth: -1
# 是否允许clang-format尝试重新粘合注释(true/false), 不建议使用
ReflowComments: false
# 是否移除多余的{}, 不建议
RemoveBracesLLVM: false
# 模板中的require语句位置, 建议OwnLine
RequiresClausePosition: OwnLine
# 分隔不同定义块, 建议Always, 可选:
# - Leave - 不处理, 建议, 即由业务决定, 也可以使用Always
# - Always - 永远进行分隔
# - Never: 永远 不进行, 不建议
SeparateDefinitionBlocks: Leave
# Java项, 跳过
SortJavaStaticImport: Before
# 排序using语句(true/false), 不建议开启
SortUsingDeclarations: false
# C风格cast的类型括号后面是否增加space(true/false), 比较诡异, 建议false
SpaceAfterCStyleCast: false
# 逻辑非操作(!)后面是否加space(true/false), 比较诡异, 建议false
SpaceAfterLogicalNot: false
# template关键字后面是否加space(true/false), 建议true, 即template <xxx>, 而不是template<xxx>
SpaceAfterTemplateKeyword: true
# 赋值语句操作符前是否添加space(true/false), 建议true
SpaceBeforeAssignmentOperators: true
# case语句:前是否增加space(true/false), 建议false
SpaceBeforeCaseColon: false
# c++11的统一初始化列表的大括号中是否添加space(true/false), 建议false
SpaceBeforeCpp11BracedList: false
# 构造函数初始化列表:前是否加space(true/false), 建议false
SpaceBeforeCtorInitializerColon: false
# 继承列表的:前是否加space(true/false), 建议true
SpaceBeforeInheritanceColon: true
# 圆括号前是否增加空格: 建议只在控制语句的贺括号前增加, 即配置为ControlStatements即可
SpaceBeforeParens: ControlStatements
# SpaceBeforeParens为Custom时使用
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
# 指针修饰的space添加, 建议Default, 即使用PointerAlignment代替
SpaceAroundPointerQualifiers: Default
# Loop关键字前前是否增加space, 建议true
SpaceBeforeRangeBasedForLoopColon: true
# 空body是否添加space, 建议true
SpaceInEmptyBlock: true
# 圆括号前是否增加space, 建议false, true太多影响代码紧凑
SpaceInEmptyParentheses: false
# Trailing注释前的空格数, 建议1
SpacesBeforeTrailingComments: 1
# <>里面是否增加space, 不建议, 配置成Never即可
SpacesInAngles: Never
# 条件语句()里面是否增加space, 不建议, 配置成Never即可
SpacesInConditionalStatement: false
# 容器初始化列表[]/{}里面是否增加space, 不建议(跟C++11风格保持一致)
SpacesInContainerLiterals: false
# C风格的转换()里面是否增加space, 不建议
SpacesInCStyleCastParentheses: false
# 行注释前的空格范围数量, 建议Maximum关闭, 设置成-1, 即//到你的注释内容前的空格数量至少是1, 至多是无穷
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
# 贺括号内是否加space, false
SpacesInParentheses: false
# 中括号内是否加space, false
SpacesInSquareBrackets: false
# 大括号内是否加space, false
SpaceBeforeSquareBrackets: false
# 位定义:前后是否增加空格, 可选:
# - Both: 前后都添加
# - Before: 只在前增加
# - After: 只在后增加
# - None: 不增加, 建议, 没有必要因为过多的space(s)影响代码紧凑
BitFieldColonSpacing: None
# C++标准, Latest即可
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
# Tab宽度, 建议4
TabWidth: 4
# 不使用CRLF, 强制关闭, 如果DeriveLineEnding为true却未自动决策出来, 此项用于fallback策略
UseCRLF: false
# Tab使用, 没有必要使用, 直接Never
UseTab: Never
# 空格敏感宏列表
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

23
.clang-tidy

@ -0,0 +1,23 @@
Checks: "
-*,
bugprone-*,
performance-*,
readability-*,
misc-*,
clang-analyzer-*,
cppcoreguidelines-*,
modernize-*,
performance-unnecessary-value-param,
modernize-pass-by-value,
-readability-identifier-length,
-modernize-use-trailing-return-type,
-cppcoreguidelines-magic-numbers,
-cppcoreguidelines-init-variables,
"
WarningsAsErrors: ''
HeaderFilterRegex: '.*'
AnalyzeTemporaryDtors: false
FormatStyle: file

137
include/line.hpp

@ -24,12 +24,14 @@
// real t;
// };
struct ClosestDescOnSeg {
struct ClosestDescOnSeg
{
real t;
real dis;
};
class ILine {
class ILine
{
public:
virtual ~ILine() = default;
@ -42,16 +44,26 @@ public:
virtual ClosestDescOnSeg getClosestParam(const Vec3 &p) = 0;
};
class Polyline : public ILine {
const real DISC_ARC_ANGLE = std::numbers::pi * 0.125;
class Polyline : public ILine
{
public:
using Point = Vec3;
Polyline(Pt3Array points, std::vector<real> bugles, const Vec3 &normal, bool closed = false)
: _points(std::move(points)), _bugles(std::move(bugles)), _closed(closed), _normal(normal.normalize()) {
: _points(std::move(points))
, _bugles(std::move(bugles))
, _closed(closed)
, _normal(normal.normalize())
{
assert(_points.size() >= 2);
if (closed) {
if (closed)
{
assert(_points.size() == _points.size());
} else {
}
else
{
assert(_points.size() - 1 == _points.size());
}
circularArcs.resize(_bugles.size());
@ -65,15 +77,18 @@ public:
[[nodiscard]] bool isClosed() const { return _closed; }
struct CircularArc {
struct CircularArc
{
Vec3 center;
real radius;
real theta;
real h;
Vec3 u; // dir of OA
Vec3 v; // u X v = normal
Vec3 u; // dir of OA
Vec3 v; // u X v = normal
Vec3 inCircleDir;
PtBoundaryRelation inCircleCheck(const Vec3 &pt) const {
PtBoundaryRelation inCircleCheck(const Vec3 &pt) const
{
real d = (pt - center).norm();
return d < radius ? Inside : d > radius ? Outside : OnBoundary;
}
@ -88,25 +103,29 @@ private:
std::vector<CircularArc> circularArcs;
public:
void initSegInfo() {
for (size_t i = 0; i < _bugles.size(); ++i) {
void initSegInfo()
{
for (size_t i = 0; i < _bugles.size(); ++i)
{
const Point &A = _points[i];
const Point &B = _points[(i + 1) % _points.size()];
Vec3 ABHalf = (B - A) * 0.5;
Vec3 ABNorm = ABHalf.normalize();
Vec3 QONorm = _normal.cross(ABNorm) * (abs(_bugles[i]) > 1 ? -1 : 1);
real theta = std::atan(_bugles[i]) * 4;
circularArcs[i].inCircleDir = _normal.cross(ABNorm) * (abs(_bugles[i]) > 1 ? -1 : 1);
circularArcs[i].h = ABHalf.norm() * std::tan(theta * 0.5);
circularArcs[i].center = A + ABHalf + QONorm * circularArcs[i].h;
circularArcs[i].center = A + ABHalf + circularArcs[i].inCircleDir * circularArcs[i].h;
circularArcs[i].theta = theta;
circularArcs[i].radius = (circularArcs[i].center - A).norm();
circularArcs[i].u = (A - circularArcs[i].center).normalize();
circularArcs[i].v = ABNorm.cross(circularArcs[i].u);
circularArcs[i].v = _normal.cross(circularArcs[i].u);
}
}
Vec3 eval(real param) override {
if (circularArcs.empty()) initSegInfo();
Vec3 eval(real param) override
{
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
real tOnSeg = param - seg;
const auto &arc = circularArcs[seg];
@ -114,8 +133,10 @@ public:
return arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v * std::sin(phi));
}
Vec3 der1(real param) override {
if (circularArcs.empty()) initSegInfo();
Vec3 der1(real param) override
{
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
real tOnSeg = param - seg;
const auto &arc = circularArcs[seg];
@ -123,8 +144,10 @@ public:
return arc.radius * (arc.u * -std::sin(phi) + arc.v * std::cos(phi));
}
Vec3 der2(real param) override {
if (circularArcs.empty()) initSegInfo();
Vec3 der2(real param) override
{
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
real tOnSeg = param - seg;
const auto &arc = circularArcs[seg];
@ -132,29 +155,36 @@ public:
return -arc.radius * (arc.u * std::cos(phi) + arc.v * std::cos(phi));
}
ClosestDescOnSeg getClosestParam(const Vec3 &p) override {
ClosestDescOnSeg getClosestParam(const Vec3 &p) override
{
real closestDis = std::numeric_limits<real>::max();
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 &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) {
if (dis2Seg - arc.h > closestDis)
continue;
if ((A - p).norm() < closestDis)
{
closestDis = (A - p).norm();
closestParam = i;
}
if ((B - p).norm() < closestDis) {
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) {
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) {
if (dis2InsertPt < closestDis)
{
closestDis = dis2InsertPt;
closestParam = insertParam;
}
@ -169,58 +199,77 @@ public:
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
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()) {
if (closestParam < seg - std::numeric_limits<real>::epsilon())
{
closestParam = seg;
closestDis = (_points[seg] - p).norm();
break;
} else if (closestParam > seg + 1 + std::numeric_limits<real>::epsilon()) {
}
else 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};
}
const std::vector<CircularArc> &getCircularArcs() const { return circularArcs; }
void print() const {
if (_closed) printf("Closed Polyline: \n");
else printf("Open Polyline: \n");
void print() const
{
if (_closed)
printf("Closed Polyline: \n");
else
printf("Open Polyline: \n");
printf("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());
if (i != _points.size() - 1) printf(", ");
if (i != _points.size() - 1)
printf(", ");
}
std::cout << "}" << std::endl;
printf("Bugles: {\n");
for (int i = 0; i < _bugles.size(); ++i) {
for (int i = 0; i < _bugles.size(); ++i)
{
printf("%lf", _bugles[i]);
if (i != _bugles.size() - 1) printf(", ");
if (i != _bugles.size() - 1)
printf(", ");
}
std::cout << "}" << std::endl;
}
private:
const real DISC_ARC_ANGLE = std::numbers::pi * 0.125;
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 AP = p - A;
real h = std::clamp(AP.dot(AB) / AB.dot(AB), 0., 1.);
return {h, (AP - AB * h).norm()};
}
static ClosestDescOnSeg segPtDist(const Vec2 &p, const Vec2 &A, const Vec2 &B)
{
Vec2 AB = B - A;
Vec2 AP = p - A;
real h = std::clamp(AP.dot(AB) / AB.dot(AB), 0., 1.);
return {h, (AP - AB * h).norm()};
}
};
class PolynomialLine : public ILine {
class PolynomialLine : public ILine
{
public:
Vec3 eval(real param) override { return {}; };

339
include/solid.hpp

@ -5,60 +5,80 @@
#include <vec.hpp>
#include <line.hpp>
class ISolid {
class ISolid
{
public:
virtual ~ISolid() = default;
virtual real sdf(const Vec3 &p) = 0;
};
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;
return {OP.dot(u), OP.dot(v)};
}
class IExtrudedSolid : public ISolid {
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:
Polyline _profile;
Polyline _profile; // TODO: may be replaced by const ref to profile
real _rScale;
public:
IExtrudedSolid(Polyline profile, real rScale) : _profile(std::move(profile)), _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
*/
real unsignedWindingNumberSegment(const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &refNormal) {
real unsignedWindingNumberSegment(const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &refNormal)
{
Vec3 pa = a - p;
Vec3 pb = b - p;
return std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.),
static_cast<real>(1.))) / (std::numbers::pi * 2);
return std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.), static_cast<real>(1.))) /
(std::numbers::pi * 2);
}
class ExtrudedSolidPolyline : public IExtrudedSolid {
class ExtrudedSolidPolyline : public IExtrudedSolid
{
private:
Polyline _axis;
Pt2Array _localProfile2D;
Pt2Array _localCircleCenter2D;
Pt2Array _localInCircleDir;
public:
ExtrudedSolidPolyline(Polyline profile, Polyline axis, real rScale) : IExtrudedSolid(std::move(profile), rScale),
_axis(std::move(axis)) {
ExtrudedSolidPolyline(Polyline profile, Polyline axis, real rScale)
: IExtrudedSolid(std::move(profile), rScale)
, _axis(std::move(axis))
{
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);
for (int i = 0; i < _profile.getPoints().size(); ++i) {
_localProfile2D.emplace_back(get2DRepOf3DPt(_profile.getPoints()[i] - Q, N, B, Q));
_localCircleCenter2D.emplace_back(get2DRepOf3DPt(_profile.getCircularArcs()[i].center - Q, N, B, Q));
int segCount = _profile.getPoints().size();
_localProfile2D.resize(segCount);
_localInCircleDir.resize(segCount);
_localCircleCenter2D.resize(segCount);
for (int i = 0; i < segCount; ++i)
{
_localProfile2D[i] = get2DRepOf3DPt(_profile.getPoints()[i] - Q, N, B, Q);
_localCircleCenter2D[i] = get2DRepOf3DPt(_profile.getCircularArcs()[i].center - Q, N, B, Q);
_localInCircleDir[i] = get2DRepOf3DDir(_profile.getCircularArcs()[i].inCircleDir, N, B);
}
}
real sdf(const Vec3 &p) override {
real sdf(const Vec3 &p) override
{
ClosestDescOnSeg closestDesc = _axis.getClosestParam(p);
// TNB coordinate system
auto t = closestDesc.t;
@ -67,14 +87,14 @@ public:
Vec3 B = T.cross(N);
Vec3 Q = _axis.eval(t);
Vec3 QP = p - Q;
auto uv = get2DRepOf3DPt(QP, N, B, Q);
//test it
auto p2D = get2DRepOf3DPt(QP, N, B, Q);
// TODO: to test if p2D is in _localProfile2D
for (auto i = 0; i < _localProfile2D.size(); ++i)
{
}
// TODO: to test if uv is in _localProfile2D
for (auto i = 0; i < _localProfile2D.size(); ++i) {
}
return 0;
ClosestDescOnSeg closestDescOnProfile = distance2Profile2D(p2D);
// TODO: on的情况
return closestDescOnProfile.dis * isPtInside2DProfile(p2D) ? 1 : -1;
}
private:
@ -84,99 +104,252 @@ private:
* out + in = in
* out + out = out
*/
bool isInside2DPolyline(const Polyline& outline, const Vec3& p3D, const Vec2& p2D) {
assert(outline.isClosed());
int intersectionCount = 0, segCount = outline.getBugles().size();
int onSegIdx = -1;
constexpr int numRays = 3; // 射线数量
int majorityIn = 0; // 在多边形内的射线计数
int majorityOut = 0; // 在多边形外的射线计数
for (int rayIdx = 0; rayIdx < numRays && onSegIdx == -1; ++rayIdx) {
double angle = (2.0 * std::numbers::pi * rayIdx) / numRays;
Vec2 rayDir(cos(angle), sin(angle));
int crossings = 0;
for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
if (isPointOnSegment(p2D, a, b)) {
onSegIdx = i;
break;
bool isPtInside2DProfile(const Vec2 &p2D)
{
assert(_profile.isClosed());
int segCount = _profile.getBugles().size();
// 先判断是否在outline上
// 顺便判断点-扇的位置关系
bool inFan = false;
int onLinesegButHasBugle = -1;
for (int i = 0; i < segCount; ++i)
{
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0)
{
//line segment
if (isPointOnSegment(p2D, a, b))
{
return true;
}
continue;
}
if (isPointOnSegment(p2D, a, b))
{
onLinesegButHasBugle = i;
break;
}
const auto &arc = _profile.getCircularArcs()[i];
real po = (p2D - _localCircleCenter2D[i]).norm();
if (po == arc.radius)
{
return true;
}
if (po < arc.radius && (p2D - a).dot(_localInCircleDir[i]) > 0)
{
inFan = true;
break;
}
}
// 判断点-直线多边形的关系
const auto ptInPolygon = [&](const Vec2 &p) {
int intersectionCount = 0;
// int onSegIdx = -1;
constexpr int numRays = 3; // 射线数量
int majorityIn = 0; // 在多边形内的射线计数
int majorityOut = 0; // 在多边形外的射线计数
for (int rayIdx = 0; rayIdx < numRays; ++rayIdx)
{
double angle = (2.0 * std::numbers::pi * rayIdx) / numRays;
Vec2 rayDir(cos(angle), sin(angle));
int crossings = 0;
for (int i = 0; i < segCount; ++i)
{
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
assert(isPointOnSegment(p, a, b));
// if (isPointOnSegment(p2D, a, b))
// {
// onSegIdx = i;
// break;
// }
// 使用向量方法计算射线和边的交点
double dx1 = b[0] - a[0];
double dy1 = b[1] - a[1];
double dx2 = rayDir[0];
double dy2 = rayDir[1];
double determinant = dx1 * dy2 - dy1 * dx2;
// 如果determinant为0,则射线和边平行,不计算交点
if (isEqual(determinant, 0))
continue;
double t1 = ((p[0] - a[0]) * dy2 - (p[1] - a[1]) * dx2) / determinant;
double t2 = ((p[0] - a[0]) * dy1 - (p[1] - a[1]) * dx1) / determinant;
// 检查交点是否在边上(0 <= t1 <= 1)且射线上(t2 >= 0)
if (t1 >= 0 && t1 <= 1 && t2 >= 0)
{
crossings++;
}
}
// 使用向量方法计算射线和边的交点
double dx1 = b[0] - a[0];
double dy1 = b[1] - a[1];
double dx2 = rayDir[0];
double dy2 = rayDir[1];
double determinant = dx1 * dy2 - dy1 * dx2;
// 如果determinant为0,则射线和边平行,不计算交点
if (isEqual(determinant, 0)) continue;
double t1 = ((p2D[0] - a[0]) * dy2 - (p2D[1] - a[1]) * dx2) / determinant;
double t2 = ((p2D[0] - a[0]) * dy1 - (p2D[1] - a[1]) * dx1) / determinant;
// 检查交点是否在边上(0 <= t1 <= 1)且射线上(t2 >= 0)
if (t1 >= 0 && t1 <= 1 && t2 >= 0) {
crossings++;
if (crossings % 2 == 0)
{
majorityOut++;
}
else
{
majorityIn++;
}
}
if (crossings % 2 == 0) {
majorityOut++;
} else {
majorityIn++;
return majorityIn > majorityOut;
};
if (onLinesegButHasBugle != -1)
{
// 需要特殊考虑的情况
// 从p2D向inCircle方向前进一小步
Vec2 samplePt =
p2D + _localCircleCenter2D[onLinesegButHasBugle] * std::numeric_limits<real>::epsilon() * 1e6;
return !ptInPolygon(samplePt); // 取反
}
return ptInPolygon(p2D) ^ inFan;
// TODO: 返回on的情况
}
ClosestDescOnSeg distance2Profile2D(const Vec2 &p2D)
{
// TODO: 2D 下点到圆弧的距离应该可以直接算,不用这么迭代!
assert(_profile.isClosed());
real closestDis = std::numeric_limits<real>::max();
real closestParam;
int segCount = _profile.getBugles().size();
for (int i = 0; i < segCount; ++i)
{
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
const auto &arc = _profile.getCircularArcs()[i];
real dis2Seg = Polyline::segPtDist(p2D, a, b).dis;
if (dis2Seg - arc.h > closestDis)
continue;
if ((a - p2D).norm() < closestDis)
{
closestDis = (a - p2D).norm();
closestParam = i;
}
if ((b - p2D).norm() < closestDis)
{
closestDis = (b - p2D).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;
real dis2InsertPt = (p2D - eval(insertParam)).norm();
if (dis2InsertPt < closestDis)
{
closestDis = dis2InsertPt;
closestParam = insertParam;
}
}
}
// 判断是否在扇内
bool inFan = false;
for (int i = 0; i < segCount; ++i) {
const Vec2& a = _localProfile2D[i];
const Vec2& b = _localProfile2D[(i + 1) % segCount];
real po = (p2D - _localCircleCenter2D[i]).norm();
if (po == _profile.getCircularArcs()[i].radius) {
// TODO
// 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
Vec2 q = eval(closestParam);
Vec2 qDer1 = der1(closestParam);
Vec2 qDer2 = der2(closestParam);
real lDer1 = (q - p2D).dot(qDer1);
int iter = 0;
while (abs(lDer1) > std::numeric_limits<real>::epsilon() * 1e6)
{
closestParam -= lDer1 / (qDer1.dot(qDer1) + (q - p2D).dot(qDer2)); // -der1 / der2
q = eval(closestParam);
qDer1 = der1(closestParam);
qDer2 = der2(closestParam);
lDer1 = (q - p2D).dot(qDer1);
printf("After iter %d, dL is %lf\n", iter, lDer1);
if (closestParam < seg - std::numeric_limits<real>::epsilon())
{
closestParam = seg;
closestDis = (_localProfile2D[seg] - p2D).norm();
break;
}
else if (closestParam > seg + 1 + std::numeric_limits<real>::epsilon())
{
closestParam = seg + 1;
closestDis = (_localProfile2D[(seg + 1) % segCount] - p2D).norm();
break;
}
if ((po < _profile.getCircularArcs()[i].radius) {
if ((p2D - a).dot(b - a) > 0) {
inFan = true;
break;
closestDis = (q - p2D).norm();
iter++;
}
return {closestDis, closestParam};
}
bool isOn2DPolyline(const Vec2 &p2D)
{
int segCount = _profile.getBugles().size();
for (int i = 0; i < segCount; ++i)
{
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0)
{
//line segment
if (isPointOnSegment(p2D, a, b))
{
return true;
}
continue;
}
}
}
bool isPointOnSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b) {
bool isPointOnSegment(const Vec2 p, const Vec2 &a, const Vec2 &b)
{
// check collinearity
double crossProduct = (p[1] - a[1]) * (b[0] - a[0]) - (p[0] - a[0]) * (b[1] - a[1]);
if (!isEqual(crossProduct, 0)) return false; // Not collinear
if (!isEqual(crossProduct, 0))
return false; // Not collinear
// Check if point is within segment bounds
return (p[0] >= std::min(a[0], b[0]) && p[0] <= std::max(a[0], b[0]) &&
p[1] >= std::min(a[1], b[1]) && p[1] <= std::max(a[1], b[1]));
return (p[0] >= std::min(a[0], b[0]) && p[0] <= std::max(a[0], b[0]) && p[1] >= std::min(a[1], b[1]) &&
p[1] <= std::max(a[1], b[1]));
}
real wnCircularArc(const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &plgNormal,
const Polyline::CircularArc &arc, int dir) {
real wnCircularArc(
const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &plgNormal, const Polyline::CircularArc &arc, int dir)
{
Vec3 pa = a - p;
Vec3 pb = b - p;
real wn = std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.),
static_cast<real>(1.))) / (std::numbers::pi * 2);
real wn =
std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast<real>(-1.), static_cast<real>(1.))) /
(std::numbers::pi * 2);
auto inOutCircle = arc.inCircleCheck(p);
if (inOutCircle == PtBoundaryRelation::Outside || pa.cross(pb).dot(plgNormal) < 0) {
if (inOutCircle == PtBoundaryRelation::Outside || pa.cross(pb).dot(plgNormal) < 0)
{
// outside
// pa.cross(pb).dot(plgNormal) 不会 == 0
return -wn * dir;
}
if (inOutCircle == PtBoundaryRelation::Inside) {
if (inOutCircle == PtBoundaryRelation::Inside)
{
return wn * dir;
}
return 0;
}
// Vec2 eval2DProfile(real param)
// {
// int seg = static_cast<int>(param);
// real tOnSeg = param - seg;
// const auto &arc = circularArcs[seg];
// real phi = tOnSeg * arc.theta;
// return arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v * std::sin(phi));
// }
};
class ExtrudedSolidPolynomialLine : public IExtrudedSolid {
class ExtrudedSolidPolynomialLine : public IExtrudedSolid
{
protected:
PolynomialLine _axis;
};

Loading…
Cancel
Save