Browse Source

onBoudary cases & 2d arc sdf

master
gjj 1 year ago
parent
commit
95046779c1
  1. 691
      .clang-format
  2. 1
      .clang-tidy
  3. 5
      CMakeLists.txt
  4. 9
      include/common.hpp
  5. 153
      include/line.hpp
  6. 297
      include/solid.hpp
  7. 45
      include/vec.hpp
  8. 2
      main.cpp
  9. 52
      require.md

691
.clang-format

@ -1,409 +1,362 @@
---
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: LLVM
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
#访问声明符缩进
# this style configuration is based on google style configuration.
# The following configuration is different from the basic configuration.
# 缩进宽度
IndentWidth: 4
# 访问权限说明符(public/private等)的偏移
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
# 大括号换行
# # 开括号(开圆括号、尖括号、方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
# AlignAfterOpenBracket: Align
# # 连续赋值时,对齐所有等号
# AlignConsecutiveAssignments: false
# # 连续声明时,对齐所有声明的变量名
# AlignConsecutiveDeclarations: false
# 反斜杆换行的对齐方式
# -- DontAlign - 不进行对齐
# -- Left - 反斜杠靠左对齐
# -- Right - 反斜杠靠右对齐
# AlignEscapedNewlines: Right
# 二元、三元表达式的对齐方式(当表达式需要占用多行时)
# -- DontAlign - 不进行对齐
# -- Align - 从操作符开始对齐
# -- AlignAfterOperator - 从操作数开始对齐
AlignOperands: true
# # 是否对齐行尾注释
# AlignTrailingComments: true
# # 函数声明的所有参数在放在下一行
# AllowAllParametersOfDeclarationOnNextLine: false
# # 是否允许短的代码块放在同一行
# AllowShortBlocksOnASingleLine: false
# # 短的case标签和语句放在同一行
# AllowShortCaseLabelsOnASingleLine: true
# # 短的函数放在同一行
# -- None - 不把短的函数放在同一行
# -- InlineOnly - 只把类内的内联函数放在同一行,全局的空函数不放在同一行
# -- Empty - 只把空的函数放在同一行
# -- Inline - 把类内的内联函数放在同一行,全局的空函数不放在同一行
# -- All - 都允许放在同一行
AllowShortFunctionsOnASingleLine: InlineOnly
# # 短的if语句保持在同一行
# AllowShortIfStatementsOnASingleLine: true
# # 短的循环保持在同一行
# AllowShortLoopsOnASingleLine: true
# # 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
# # AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
# AlwaysBreakAfterReturnType: None
# # 总是在多行string字面量前换行
# AlwaysBreakBeforeMultilineStrings: true
# # 总是在template声明后换行
AlwaysBreakTemplateDeclarations: true
# # 函数调用时,参数的放置规则
# -- false - 参数要么放在同一行,要么每个参数占用一行
# -- true - 不做强制要求
# BinPackArguments: true
# 函数声明、定义时,参数的放置规则
# -- false - 参数要么放在同一行,要么每个参数占用一行
# -- true - 不做强制要求
# BinPackParameters: true
# 大括号放置风格
# -- Attach - 大括号紧随前方内容,放在同一行
# -- Linux - 与 Attach 类似,除了 函数、命名空间、类定义 的大括号放在下一行
# -- Mozilla - 与 Attach 类似,除了枚举、函数、结构(class\struct\union)的大括号放在下一行
# -- Stroustrup - 与 Attach 类似,但函数定义前、catch前方、else前方的"{}"放在单独一行
# -- Allman - 总是换行
# -- Whitesmiths - 类似 Allman,但"{}"和内部的语句对齐到同样位置
# -- GNU - 总是换行,但在控制语句后的"{}"总是对齐到下一个位置
# -- WebKit - 与 Attach 类似,但在函数定义前换行
# -- Custom - 依赖 BraceWrapping
# 注:这里认为语句块也属于函数
BreakBeforeBraces: Attach
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
BraceWrapping:
# 在case后的大括号是否换行
AfterCaseLabel: true
# class后
AfterClass: true
# 控制语句(if/for/while/switch/...)后是否换行
# - Never: 永远不, 即永远将语句体的大括号放置于控制语句同一行
# - MultiLine: 多行控制语句才进行换行
# - Always: 永远换行, 建议
AfterControlStatement: Always
# 下面比较容易理解, 不再作无意义的解释
AfterEnum: true
# class定义后面
AfterClass: false
# 控制语句后面
AfterControlStatement: false
# enum定义后面
AfterEnum: false
# 函数定义后面
AfterFunction: true
# 命名空间定义后面
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
# ObjC定义后面
AfterObjCDeclaration: false
# struct定义后面
AfterStruct: false
# union定义后面
AfterUnion: false
# extern之后
AfterExternBlock: true
# catch之前
BeforeCatch: false
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
# else之前
BeforeElse: false
# 继续缩进大括号
IndentBraces: false
# 分离空函数
# # 当空白函数的"{}"和函数名称不需要放在同一行时,是否拆分函数体
SplitEmptyFunction: true
# 分离空语句
# # 当空白结构(class\struct\union)的"{}"需要放在单独的行时,是否拆分"{}"
SplitEmptyRecord: true
# 分离空命名空间
# # 当空白的命名空间的"{}"需要放在单独的行时,是否拆分"{}"
SplitEmptyNamespace: true
# 二元操作符前是否换行, 建议为None
BreakBeforeBinaryOperators: None
# 概念声明前是否换行, 建议Always
BreakBeforeConceptDeclarations: Always
# 大括号换行风格,Custom即可, 具体值可参考上方文档
BreakBeforeBraces: Custom
# 继承列表括号前换行, false即可
BreakBeforeInheritanceComma: false
# 是否将整个继承列表换行
BreakInheritanceList: BeforeColon
# # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
BreakBeforeBinaryOperators: NonAssignment
# BreakBeforeInheritanceComma: false
# # 在三元运算符前换行
# 当三元表达式不能放在同一行时,是否在三元操作符前方换行
# -- true - 操作符位于新行的首部
# -- false - 操作符位于上一行的尾部
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
# 未在文档中找到
# BreakConstructorInitializersBeforeComma: false
# # 构造函数初始化列表分割方式
# -- BeforeColon - 在冒号 ':' 前方分割,冒号位于行首,逗号','位于行尾
# -- BeforeComma - 在冒号和逗号前方分割,冒号和逗号都位于行首,并且对齐
# -- AfterColon - 在冒号和逗号后方分割,冒号和逗号位于行尾
BreakConstructorInitializers: BeforeColon
# # 是否在每个java注解后方换行
# BreakAfterJavaFieldAnnotations: false
# # 是否分割过长的字符串
# BreakStringLiterals: false
# # 每行字符长度的限制,0表示没有限制
ColumnLimit: 100
# # 用于匹配注释信息的正则表达式,被匹配的行不会做任何修改
# CommentPragmas: '^ IWYU pragma:'
# 是否压缩紧接的命名空间
# -- true - 将紧跟的命名空间放在同一行
# -- false - 每个命名空间位于新的一行
CompactNamespaces: false
# 构造函数初始化列表缩进, 建议0
ConstructorInitializerIndentWidth: 0
# 函数调用续行对齐, 建议4
# # 构造函数的初始化列表要么都在同一行,要么都各自一行
# -- true - 如果可能,初始化列表放在同一行;如果不满足长度选择,则每个单独放一行
# -- false - 初始化列表可以随意放置
# ConstructorInitializerAllOnOneLineOrOnePerLine: false
# # 构造函数的初始化列表和基类集成列表的对齐宽度
ConstructorInitializerIndentWidth: 4
# # 延续语句的对齐宽度
ContinuationIndentWidth: 4
# C++11的统一初始化列表大括号风格, 建议true
# # 去除C++11的列表初始化的大括号{后和}前的空格
Cpp11BracedListStyle: true
# 提取行结束符并标准化, 建议false, 不要进行分析及自动运用, 而是强制使用UseCRLF设定来做
DeriveLineEnding: true
# 是否开启文件分析, 根据文件中的*/&使用情况更新clang-format设定, 在无法决定时, 使用PointerAlignment代替, 不建议开启
DerivePointerAlignment: false
# 是否自动分析指针的对齐方式
# -- true - 自动分析并使用指针的对齐方式,若无法分析,则使用 PointerAlignment
# -- false - 不自动分析
# DerivePointerAlignment: true
# 是否禁用格式化
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
# ExperimentalAutoDetectBinPacking: false
# 是否自动修正命名空间的结束注释
# -- true - 在短的命名空间尾部,自动添加或修改错误的命名空间结束注释
# -- false - 不自动修正
FixNamespaceComments: true
# 大于多少行namespace内的代码行时才在namespace结尾添加 // namespace xxx, 建议0,即无论如何都添加
ShortNamespaceLines: 0
# Macro宏
# foreach 循环
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
#If宏
IfMacros:
- KJ_IF_MAYBE
# include代码块操作, 前提是SortIncludes开启:
# - Preserve: 只对每个代码块排序
# - Merge: 对所有代码块合并, 并在合并后排序
# - Regroup: 对所有include块进行分析, 并重新分块, 不建议!
# 多个 include 块(有空行分隔的include)排序时的分组规则
# -- Preserve - 保留原有的块分隔,各自排序
# -- Merge - 将所有的块视为同一个,然后进行排序
# -- Regroup - 将所有的块视为同一个进行排序,然后按照 IncludeCategories 的规则进行分组
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相关, 跳过
# IncludeCategories:
# - Regex: '^<ext/.*\.h>'
# Priority: 2
# - Regex: '^<.*\.h>'
# Priority: 1
# - Regex: '^<.*'
# Priority: 2
# - Regex: '.*'
# Priority: 3
# IncludeIsMainRegex: '([-_](test|unittest))?$'
# # 缩进case标签
# -- true - case 不与 switch 对齐
# -- false - case 和 switch 对齐
IndentCaseLabels: true
# 预处理命令(#if\#ifdef\#endif等)的缩进规则
# -- None - 不进行缩进
# -- AfterHash - 在前导'#'后缩进,'#'放在最左侧,之后的语句参与缩进
# -- BeforeHash - 在前导'#'前进行缩进
IndentPPDirectives: AfterHash
# # 函数返回类型换行时,缩进函数声明或函数定义的函数名
# IndentWrappedFunctionNames: false
# JavaScript 中的字符串引号规则
# -- Leave - 保持原样
# -- Single - 全部使用单引号
# -- Double - 全部使用双引号
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
# 是否block开始前有一个empty line, 诡异, 直接false
KeepEmptyLinesAtTheStartOfBlocks: false
# 未找到定义
LambdaBodyIndentation: Signature
# 宏开始的正则, 不使用
MacroBlockBegin: ''
# 宏结束的正则, 不使用
MacroBlockEnd: ''
# 空行保持, 建议为1
# 是否在 JavaScript 的 import/export 语句后换行
# JavaScriptWrapImports: true
# # 保留在块开始处的空行
# -- true - 保留块起始的空行
# -- false - 删除块起始的空行
KeepEmptyLinesAtTheStartOfBlocks: true
# 用于识别宏定义型块起始的正则表达式
# MacroBlockBegin: ''
# 用于识别宏定义型块结束的正则表达式
# MacroBlockEnd: ''
# # 连续空行的最大数量
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
# # 命名空间内部的缩进规则
# -- None - 都不缩进
# -- Inner - 只缩进嵌套的命名空间内容
# -- All - 缩进所有命名空间内容
NamespaceIndentation: Inner
# Objective-C 相关配置
# ObjCBlockIndentWidth: 2
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: false
# PenaltyBreakAssignment: 2
# PenaltyBreakBeforeFirstCallParameter: 1
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 200
# # 指针和引用(*和&)的对齐规则
# -- Left - * 靠近左侧
# -- Right - * 靠近右侧
# -- Middle - * 放在中间
# NOTE : 在 SpaceAroundPointerQualifiers 为 Default,
# 且 DerivePointerAlignment 失效后启用
PointerAlignment: Right
# 引用对齐, 可选:
# - Pointer: 使用'PointerAlignment'配置, 建议使用
# - Left: Left
# - Right: Right
ReferenceAlignment: Pointer
# 预处理对齐宽度
PPIndentWidth: -1
# 是否允许clang-format尝试重新粘合注释(true/false), 不建议使用
# RawStringFormats:
# - Delimiter: pb
# Language: TextProto
# BasedOnStyle: google
# # 重新排版注释
ReflowComments: false
# 是否移除多余的{}, 不建议
RemoveBracesLLVM: false
# 模板中的require语句位置, 建议OwnLine
RequiresClausePosition: OwnLine
# 分隔不同定义块, 建议Always, 可选:
# - Leave - 不处理, 建议, 即由业务决定, 也可以使用Always
# - Always - 永远进行分隔
# - Never: 永远 不进行, 不建议
SeparateDefinitionBlocks: Leave
# Java项, 跳过
SortJavaStaticImport: Before
# 排序using语句(true/false), 不建议开启
# # 重新排序#include
# -- Never - 不进行排序
# -- CaseSensitive - 排序时大小写敏感
# -- CaseInsensitive - 排序时大小写不敏感
SortIncludes: false
# java 中静态 import 的排序规则
# -- Before - 静态放在非静态前方
# -- After - 静态放在非静态后方
# SortJavaStaticImport: Before
# # 重新排序using声明
SortUsingDeclarations: false
# C风格cast的类型括号后面是否增加space(true/false), 比较诡异, 建议false
# # 在C风格类型转换后添加空格
SpaceAfterCStyleCast: false
# 逻辑非操作(!)后面是否加space(true/false), 比较诡异, 建议false
SpaceAfterLogicalNot: false
# template关键字后面是否加space(true/false), 建议true, 即template <xxx>, 而不是template<xxx>
# # 在Template关键字后面添加空格
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即可
# # 在 C++11 的初始化列表前加空格
# SpaceBeforeCpp11BracedList: true
# 在构造函数的初始化冒号":"前加空格
# SpaceBeforeCtorInitializerColon: true
# 在构造函数的继承冒号":"前加空格
# SpaceBeforeInheritanceColon: true
# 小括号"()"前加空格的规则
# -- Never - 从不加空格
# -- ControlStatements - 只在控制语句(for/if/while...)时加空格
# -- ControlStatementsExceptForEachMacros - 类型 ControlStatements,只是不再 ForEach 后加空格
# -- Always - 总是添加空格
# -- NonEmptyParentheses - 类似 Always,只是不再空白括号前加空格
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太多影响代码紧凑
# 在 for 循环的冒号":"前加空格
# SpaceBeforeRangeBasedForLoopColon: true
# # 在空白的小括号"()"中添加空格
SpaceInEmptyParentheses: false
# Trailing注释前的空格数, 建议1
# # 在行尾的注释前添加的空格数(只适用于//)
SpacesBeforeTrailingComments: 1
# <>里面是否增加space, 不建议, 配置成Never即可
SpacesInAngles: Never
# 条件语句()里面是否增加space, 不建议, 配置成Never即可
SpacesInConditionalStatement: false
# 容器初始化列表[]/{}里面是否增加space, 不建议(跟C++11风格保持一致)
SpacesInContainerLiterals: false
# C风格的转换()里面是否增加space, 不建议
# # 在尖括号的"<"后,和">"前添加空格
SpacesInAngles: false
# # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
SpacesInContainerLiterals: true
# # 在C风格类型转换的括号中添加空格
SpacesInCStyleCastParentheses: false
# 行注释前的空格范围数量, 建议Maximum关闭, 设置成-1, 即//到你的注释内容前的空格数量至少是1, 至多是无穷
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
# 贺括号内是否加space, false
# # 在圆括号的"("后,和")"前添加空格
SpacesInParentheses: false
# 中括号内是否加space, false
# 在中括号中加空格
# 当中括号内没有数据时,不受本规则影响(如空白的lambda 捕获表、不定长度的数组声明)
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
# 语言标准: Cpp03, Cpp11, Auto
Standard: Auto
# # tab宽度
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
...
# # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
UseTab: Never
---

1
.clang-tidy

@ -5,6 +5,7 @@ Checks: "
readability-*,
misc-*,
clang-analyzer-*,
clang-diagnostic-*,
cppcoreguidelines-*,
modernize-*,

5
CMakeLists.txt

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.27)
cmake_minimum_required(VERSION 3.25)
project(PMClassifier)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
include_directories(include)
include_directories(./include)
add_executable(PMClassifier main.cpp)

9
include/common.hpp

@ -1,11 +1,8 @@
#pragma once
enum PtBoundaryRelation {
Inside = 1,
OnBoundary,
Outside = -1
};
enum PtBoundaryRelation { Inside = -1, OnBoundary, Outside = 1 };
bool isEqual(double a, double b) {
// TODO
return a == b; }
return a == b;
}

153
include/line.hpp

@ -1,14 +1,15 @@
#pragma once
#include "common.hpp"
#include "vec.hpp"
#include <vector>
#include <iostream>
#include <memory>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <limits.h>
#include <memory>
#include <numbers>
#include "common.hpp"
#include <vector>
// class ILineParam {
// public:
@ -24,14 +25,14 @@
// real t;
// };
struct ClosestDescOnSeg
{
struct ClosestDescOnSeg {
real t;
real dis;
ClosestDescOnSeg(real _t, real _dis) : t(_t), dis(_dis) {}
ClosestDescOnSeg() : t(0), dis(std::numeric_limits<real>::max()) {}
};
class ILine
{
class ILine {
public:
virtual ~ILine() = default;
@ -44,26 +45,35 @@ public:
virtual ClosestDescOnSeg getClosestParam(const Vec3 &p) = 0;
};
template <typename VecType> // Vec2 or Vec3
struct CircularArc {
VecType center;
real radius;
real theta;
real h;
VecType u;
VecType v;
VecType inCircleDir;
PtBoundaryRelation inCircleCheck(const VecType &pt) const {
real d = (pt - center).norm();
return d < radius ? Inside : d > radius ? Outside : OnBoundary;
}
};
;
const real DISC_ARC_ANGLE = std::numbers::pi * 0.125;
class Polyline : public ILine
{
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());
@ -77,36 +87,17 @@ public:
[[nodiscard]] bool isClosed() const { return _closed; }
struct CircularArc
{
Vec3 center;
real radius;
real theta;
real h;
Vec3 u; // dir of OA
Vec3 v; // u X v = normal
Vec3 inCircleDir;
PtBoundaryRelation inCircleCheck(const Vec3 &pt) const
{
real d = (pt - center).norm();
return d < radius ? Inside : d > radius ? Outside : OnBoundary;
}
};
private:
Pt3Array _points;
std::vector<real> _bugles;
Vec3 _normal;
bool _closed;
std::vector<CircularArc> circularArcs;
std::vector<CircularArc<Vec3>> 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;
@ -122,8 +113,7 @@ public:
}
}
Vec3 eval(real param) override
{
Vec3 eval(real param) override {
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
@ -133,8 +123,7 @@ public:
return arc.center + arc.radius * (arc.u * std::cos(phi) + arc.v * std::sin(phi));
}
Vec3 der1(real param) override
{
Vec3 der1(real param) override {
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
@ -144,8 +133,7 @@ public:
return arc.radius * (arc.u * -std::sin(phi) + arc.v * std::cos(phi));
}
Vec3 der2(real param) override
{
Vec3 der2(real param) override {
if (circularArcs.empty())
initSegInfo();
int seg = static_cast<int>(param);
@ -155,36 +143,30 @@ 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 ((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;
}
@ -192,29 +174,26 @@ public:
}
// 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
// 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)
{
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())
{
if (closestParam > seg + 1 + std::numeric_limits<real>::epsilon()) {
closestParam = seg + 1;
closestDis = (_points[(seg + 1) % _points.size()] - p).norm();
break;
@ -225,25 +204,22 @@ public:
return {closestParam, closestDis};
}
const std::vector<CircularArc> &getCircularArcs() const { return circularArcs; }
const std::vector<CircularArc<Vec3>> &getCircularArcs() const { return circularArcs; }
void print() const
{
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(", ");
}
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(", ");
@ -251,16 +227,14 @@ public:
std::cout << "}" << std::endl;
}
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)
{
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.);
@ -268,8 +242,23 @@ public:
}
};
class PolynomialLine : public ILine
{
class HelixLine : public ILine {
public:
Vec3 eval(real param) override { return {}; };
Vec3 der1(real param) override { return {}; };
Vec3 der2(real param) override { return {}; };
ClosestDescOnSeg getClosestParam(const Vec3 &p) override { return {}; };
private:
Vec3 axisStart, axisDir;
real advanceLen, advancePerRound;
real startTheta; // 轴线起始时在螺旋投影(圆)上的参数
};
class PolynomialLine : public ILine {
public:
Vec3 eval(real param) override { return {}; };

297
include/solid.hpp

@ -1,64 +1,59 @@
#pragma once
#include <real.hpp>
#include "common.hpp"
#include "real.hpp"
#include <utility>
#include <vec.hpp>
#include <line.hpp>
#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)};
}
Vec2 get2DRepOf3DDir(const Vec3 &dir, const Vec3 &u, const Vec3 &v) { return Vec2{dir.dot(u), dir.dot(v)}.normalize(); }
Vec2 get2DRepOf3DDir(const Vec3 &dir, const Vec3 &u, const Vec3 &v) {
return Vec2{dir.dot(u), dir.dot(v)}.normalize();
}
class IExtrudedSolid : public ISolid
{
class IExtrudedSolid : public ISolid {
public:
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;
std::vector<CircularArc<Vec2>> _localArcs2d;
// Pt2Array _localCircleCenter2D;
// Pt2Array _localInCircleDir;
public:
ExtrudedSolidPolyline(Polyline profile, Polyline axis, real rScale)
: IExtrudedSolid(std::move(profile), rScale)
, _axis(std::move(axis))
{
: 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();
@ -67,18 +62,20 @@ public:
Vec3 Q = _axis.eval(0);
int segCount = _profile.getPoints().size();
_localProfile2D.resize(segCount);
_localInCircleDir.resize(segCount);
_localCircleCenter2D.resize(segCount);
for (int i = 0; i < segCount; ++i)
{
_localArcs2d.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);
auto &arc2d = _localArcs2d[i];
const auto &arc3d = _profile.getCircularArcs()[i];
arc2d.center = get2DRepOf3DPt(arc3d.center - Q, N, B, Q);
arc2d.inCircleDir = get2DRepOf3DDir(arc3d.inCircleDir, N, B);
arc2d.radius = arc3d.radius;
}
}
real sdf(const Vec3 &p) override
{
real sdf(const Vec3 &p) override {
ClosestDescOnSeg closestDesc = _axis.getClosestParam(p);
// TNB coordinate system
auto t = closestDesc.t;
@ -89,12 +86,14 @@ public:
Vec3 QP = p - Q;
auto p2D = get2DRepOf3DPt(QP, N, B, Q);
// TODO: to test if p2D is in _localProfile2D
for (auto i = 0; i < _localProfile2D.size(); ++i)
{
// for (auto i = 0; i < _localProfile2D.size(); ++i) {
// }
PtBoundaryRelation ptProfileRelation = getPtProfileRelation(p2D);
if (ptProfileRelation == OnBoundary) {
return 0; // TODO: 判断OnBoundary的过程可以加一点容差
}
ClosestDescOnSeg closestDescOnProfile = distance2Profile2D(p2D);
// TODO: on的情况
return closestDescOnProfile.dis * isPtInside2DProfile(p2D) ? 1 : -1;
return closestDescOnProfile.dis * static_cast<int>(ptProfileRelation);
}
private:
@ -104,8 +103,7 @@ private:
* out + in = in
* out + out = out
*/
bool isPtInside2DProfile(const Vec2 &p2D)
{
PtBoundaryRelation getPtProfileRelation(const Vec2 &p2D) {
assert(_profile.isClosed());
int segCount = _profile.getBugles().size();
@ -113,35 +111,36 @@ private:
// 顺便判断点-扇的位置关系
bool inFan = false;
int onLinesegButHasBugle = -1;
for (int i = 0; i < segCount; ++i)
{
for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0)
{
if (_profile.getBugles()[i] == 0) {
//line segment
if (isPointOnSegment(p2D, a, b))
{
return true;
if (isPointOnSegment(p2D, a, b)) {
return OnBoundary;
}
continue;
}
if (isPointOnSegment(p2D, a, b))
{
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;
real po = (p2D - _localArcs2d[i].center).norm();
if ((p2D - a).dot(_localArcs2d[i].inCircleDir) > 0) {
if (po == arc.radius) {
return OnBoundary;
}
if (po < arc.radius && (p2D - a).dot(_localInCircleDir[i]) > 0)
{
if (po < arc.radius) {
inFan = true;
break;
}
} else {
if (po <= arc.radius) {
inFan = true;
break;
}
}
}
// 判断点-直线多边形的关系
@ -151,14 +150,12 @@ private:
constexpr int numRays = 3; // 射线数量
int majorityIn = 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;
Vec2 rayDir(cos(angle), sin(angle));
int crossings = 0;
for (int i = 0; i < segCount; ++i)
{
for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
assert(isPointOnSegment(p, a, b));
@ -182,120 +179,78 @@ private:
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)
{
if (t1 >= 0 && t1 <= 1 && t2 >= 0) {
crossings++;
}
}
if (crossings % 2 == 0)
{
if (crossings % 2 == 0) {
majorityOut++;
}
else
{
} else {
majorityIn++;
}
}
return majorityIn > majorityOut;
};
if (onLinesegButHasBugle != -1)
{
if (onLinesegButHasBugle != -1) {
// 需要特殊考虑的情况
// 从p2D向inCircle方向前进一小步
Vec2 samplePt =
p2D + _localCircleCenter2D[onLinesegButHasBugle] * std::numeric_limits<real>::epsilon() * 1e6;
return !ptInPolygon(samplePt); // 取反
Vec2 samplePt = p2D
+ _localArcs2d[onLinesegButHasBugle].center
* std::numeric_limits<real>::epsilon() * 1e6;
return !ptInPolygon(samplePt) ? Inside : Outside; // 取反
}
return ptInPolygon(p2D) ^ inFan;
return ptInPolygon(p2D) ^ inFan ? Inside : Outside;
// TODO: 返回on的情况
}
ClosestDescOnSeg distance2Profile2D(const Vec2 &p2D)
{
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;
ClosestDescOnSeg res{};
for (int i = 0; i < _localArcs2d.size(); ++i) {
auto disDesc =
distance2Arc2D(p2D, _localProfile2D[i],
_localProfile2D[(i + 1) % _localArcs2d.size()], _localArcs2d[i]);
if (res.dis > disDesc.dis) {
res.dis = disDesc.dis;
res.t = i + disDesc.t;
}
}
return res;
}
// 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;
}
closestDis = (q - p2D).norm();
iter++;
ClosestDescOnSeg distance2Arc2D(const Vec2 &p2D, const Vec2 &a, const Vec2 &b,
const CircularArc<Vec2> &arc) {
const Vec2 &center = arc.center;
Vec2 op = p2D - center;
Vec2 q = center + arc.radius * op.normalize(); // closest pt on circle
Vec2 oq = q - center;
Vec2 oa = a - center;
// 判断q是否在弧上
if ((q - a).dot(arc.inCircleDir) > 0) {
// 计算参数
real normMulti = arc.radius * oq.norm();
real cosTheta = (oa).dot(oq) / normMulti;
real sinTheta = (oa).cross(oq) / normMulti;
return {atan2(sinTheta, cosTheta), (p2D - q).norm()};
}
return {closestDis, closestParam};
real paDis = (a - p2D).norm();
real pbDis = (b - p2D).norm();
if (paDis < pbDis)
return {0, paDis};
return {1, pbDis};
}
bool isOn2DPolyline(const Vec2 &p2D)
{
bool isOn2DPolyline(const Vec2 &p2D) {
int segCount = _profile.getBugles().size();
for (int i = 0; i < segCount; ++i)
{
for (int i = 0; i < segCount; ++i) {
const Vec2 &a = _localProfile2D[i];
const Vec2 &b = _localProfile2D[(i + 1) % segCount];
if (_profile.getBugles()[i] == 0)
{
if (_profile.getBugles()[i] == 0) {
//line segment
if (isPointOnSegment(p2D, a, b))
{
if (isPointOnSegment(p2D, a, b)) {
return true;
}
continue;
@ -303,40 +258,39 @@ private:
}
}
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
// 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)
{
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);
auto inOutCircle = arc.inCircleCheck(p);
if (inOutCircle == PtBoundaryRelation::Outside || pa.cross(pb).dot(plgNormal) < 0)
{
// outside
// pa.cross(pb).dot(plgNormal) 不会 == 0
return -wn * dir;
}
if (inOutCircle == PtBoundaryRelation::Inside)
{
return wn * 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);
// auto inOutCircle = arc.inCircleCheck(p);
// if (inOutCircle == PtBoundaryRelation::Outside || pa.cross(pb).dot(plgNormal) < 0)
// {
// // outside
// // pa.cross(pb).dot(plgNormal) 不会 == 0
// return -wn * dir;
// }
// if (inOutCircle == PtBoundaryRelation::Inside)
// {
// return wn * dir;
// }
return 0;
}
// return 0;
// }
// Vec2 eval2DProfile(real param)
// {
@ -348,8 +302,7 @@ private:
// }
};
class ExtrudedSolidPolynomialLine : public IExtrudedSolid
{
class ExtrudedSolidPolynomialLine : public IExtrudedSolid {
protected:
PolynomialLine _axis;
};

45
include/vec.hpp

@ -5,15 +5,15 @@
#include <array>
#include <assert.h>
#include <vector>
#include <stdexcept>
#include <concepts>
template <typename Derived, size_t N>
class VecBase {
public:
std::array<real, N> data;
VecBase() {
data.fill(0);
}
VecBase() { data.fill(0); }
VecBase(std::initializer_list<real> values) {
std::copy(values.begin(), values.end(), data.begin());
@ -24,9 +24,7 @@ public:
static_assert(sizeof...(args) == N, "Argument count must match vector size.");
}
explicit VecBase(const VecBase &v) {
data = v.data;
}
explicit VecBase(const VecBase &v) { data = v.data; }
VecBase &operator=(VecBase v) {
*this = std::move(v);
@ -34,12 +32,14 @@ public:
}
real &operator[](size_t index) {
if (index >= N) throw std::out_of_range("Index out of range");
if (index >= N)
throw std::out_of_range("Index out of range");
return data[index];
}
const real &operator[](size_t index) const {
if (index >= N) throw std::out_of_range("Index out of range");
if (index >= N)
throw std::out_of_range("Index out of range");
return data[index];
}
@ -75,7 +75,6 @@ public:
return result;
}
Derived operator/(real s) const {
Derived result;
for (size_t i = 0; i < N; ++i) {
@ -92,17 +91,11 @@ public:
return sum;
}
real norm() const {
return std::sqrt(dot(*this));
}
real norm() const { return std::sqrt(dot(*this)); }
Derived normalize() const {
return static_cast<Derived>(*this / norm());
}
Derived normalize() const { return static_cast<Derived>(*this / norm()); }
Derived reflect(const Derived &n) const {
return static_cast<Derived>(*this - n * 2 * dot(n));
}
Derived reflect(const Derived &n) const { return static_cast<Derived>(*this - n * 2 * dot(n)); }
};
// Vec<2> 特化
@ -128,8 +121,9 @@ public:
const real &u() const { return data[0]; }
const real &v() const { return data[1]; }
};
real cross(const Vec2 &v) const { return x() * v.y() - v.x() * y(); }
};
class Vec3 : public VecBase<Vec3, 3> {
public:
@ -166,6 +160,19 @@ public:
using Pt3Array = std::vector<Vec3>;
using Pt2Array = std::vector<Vec2>;
template <typename T>
concept IsVec2 = requires(T t) {
t.x();
t.y();
};
template <typename T>
concept IsVec3 = requires(T t) {
t.x();
t.y();
t.z();
};
//class Vec3 {
//public:
// real x, y, z;

2
main.cpp

@ -1,5 +1,5 @@
#include <iostream>
#include <solid.hpp>
#include "solid.hpp"
// TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.

52
require.md

@ -26,3 +26,55 @@ virtual CPmSolid * extrude(const CArray<PmDbPolyline *> & polys,
double scaleFactor = 1.0,
double twistAngle = 0.0) override;
```
```C++
/**
* @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;
```
```C++
/**
* @param axis_start axis start position.
* @param axis_end axis end position.
* @param start_dir vector from axis_start to helix star
* @param radius radius of helix.
* @param pith distance between threads along axis.
* @param handiness
*/
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);
```
Loading…
Cancel
Save