From 95046779c126ec22de3087ed22a9edb832610fe6 Mon Sep 17 00:00:00 2001 From: gjj Date: Tue, 19 Nov 2024 14:07:37 +0800 Subject: [PATCH] onBoudary cases & 2d arc sdf --- .clang-format | 701 +++++++++++++++++++++------------------------ .clang-tidy | 1 + CMakeLists.txt | 5 +- include/common.hpp | 9 +- include/line.hpp | 153 +++++----- include/solid.hpp | 305 +++++++++----------- include/vec.hpp | 87 +++--- main.cpp | 2 +- require.md | 52 ++++ 9 files changed, 634 insertions(+), 681 deletions(-) diff --git a/.clang-format b/.clang-format index 394316f..cabeabf 100644 --- a/.clang-format +++ b/.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 - AfterFunction: true - AfterNamespace: true - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true + # class定义后面 + AfterClass: false + # 控制语句后面 + AfterControlStatement: false + # enum定义后面 + AfterEnum: false + # 函数定义后面 + AfterFunction: true + # 命名空间定义后面 + AfterNamespace: true + # ObjC定义后面 + AfterObjCDeclaration: false + # struct定义后面 + AfterStruct: false + # union定义后面 + AfterUnion: false + # extern之后 AfterExternBlock: true - BeforeCatch: false - BeforeElse: true - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false + # catch之前 + BeforeCatch: 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: '^' +# 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 , 而不是template + +# # 在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 -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 -... +# 语言标准: Cpp03, Cpp11, Auto +Standard: Auto + +# # tab宽度 +TabWidth: 4 + +# # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always +UseTab: Never +--- diff --git a/.clang-tidy b/.clang-tidy index 83445c6..eaa025e 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -5,6 +5,7 @@ Checks: " readability-*, misc-*, clang-analyzer-*, + clang-diagnostic-*, cppcoreguidelines-*, modernize-*, diff --git a/CMakeLists.txt b/CMakeLists.txt index 43563c7..3fc4432 100644 --- a/CMakeLists.txt +++ b/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) diff --git a/include/common.hpp b/include/common.hpp index 4ed9e72..64c79b5 100644 --- a/include/common.hpp +++ b/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; } \ No newline at end of file + return a == b; +} \ No newline at end of file diff --git a/include/line.hpp b/include/line.hpp index 7385dfd..c0214af 100644 --- a/include/line.hpp +++ b/include/line.hpp @@ -1,14 +1,15 @@ #pragma once +#include "common.hpp" #include "vec.hpp" -#include -#include -#include +#include #include #include -#include +#include +#include +#include #include -#include "common.hpp" +#include // 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::max()) {} }; -class ILine -{ +class ILine { public: virtual ~ILine() = default; @@ -44,26 +45,35 @@ public: virtual ClosestDescOnSeg getClosestParam(const Vec3 &p) = 0; }; +template // 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 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 _bugles; Vec3 _normal; bool _closed; - std::vector circularArcs; + std::vector> 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(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(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(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::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(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::epsilon() * 1e6) - { + while (abs(lDer1) > std::numeric_limits::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::epsilon()) - { + if (closestParam < seg - std::numeric_limits::epsilon()) { closestParam = seg; closestDis = (_points[seg] - p).norm(); break; } - else if (closestParam > seg + 1 + std::numeric_limits::epsilon()) - { + if (closestParam > seg + 1 + std::numeric_limits::epsilon()) { closestParam = seg + 1; closestDis = (_points[(seg + 1) % _points.size()] - p).norm(); break; @@ -225,25 +204,22 @@ public: return {closestParam, closestDis}; } - const std::vector &getCircularArcs() const { return circularArcs; } + const std::vector> &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 {}; }; diff --git a/include/solid.hpp b/include/solid.hpp index 8c31453..5c4fde9 100644 --- a/include/solid.hpp +++ b/include/solid.hpp @@ -1,64 +1,59 @@ #pragma once -#include +#include "common.hpp" +#include "real.hpp" #include -#include -#include +#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(-1.), static_cast(1.))) / - (std::numbers::pi * 2); + return std::acos(std::clamp(pa.dot(pb) / (pa.norm() * pb.norm()), static_cast(-1.), + static_cast(1.))) + / (std::numbers::pi * 2); } -class ExtrudedSolidPolyline : public IExtrudedSolid -{ +class ExtrudedSolidPolyline : public IExtrudedSolid { private: Polyline _axis; Pt2Array _localProfile2D; - Pt2Array _localCircleCenter2D; - Pt2Array _localInCircleDir; + std::vector> _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(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,34 +111,35 @@ 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; - } - if (po < arc.radius && (p2D - a).dot(_localInCircleDir[i]) > 0) - { - inFan = true; - break; + 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) { + 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::epsilon() * 1e6; - return !ptInPolygon(samplePt); // 取反 + Vec2 samplePt = p2D + + _localArcs2d[onLinesegButHasBugle].center + * std::numeric_limits::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::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; } } - // TODO: 为了鲁棒和精度,应该在每个可能最近的seg上做newton iteration - int seg = static_cast(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::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::epsilon()) - { - closestParam = seg; - closestDis = (_localProfile2D[seg] - p2D).norm(); - break; - } - else if (closestParam > seg + 1 + std::numeric_limits::epsilon()) - { - closestParam = seg + 1; - closestDis = (_localProfile2D[(seg + 1) % segCount] - p2D).norm(); - break; - } - closestDis = (q - p2D).norm(); - iter++; + return res; + } + + ClosestDescOnSeg distance2Arc2D(const Vec2 &p2D, const Vec2 &a, const Vec2 &b, + const CircularArc &arc) { + const Vec2 ¢er = 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(-1.), static_cast(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(-1.), static_cast(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; }; diff --git a/include/vec.hpp b/include/vec.hpp index 374cfec..fdd5980 100644 --- a/include/vec.hpp +++ b/include/vec.hpp @@ -5,28 +5,26 @@ #include #include #include +#include +#include -template +template class VecBase { public: std::array data; - VecBase() { - data.fill(0); - } + VecBase() { data.fill(0); } VecBase(std::initializer_list values) { std::copy(values.begin(), values.end(), data.begin()); } - template + template explicit VecBase(Args... args) : data{static_cast(args)...} { 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(*this / norm()); - } + Derived normalize() const { return static_cast(*this / norm()); } - Derived reflect(const Derived &n) const { - return static_cast(*this - n * 2 * dot(n)); - } + Derived reflect(const Derived &n) const { return static_cast(*this - n * 2 * dot(n)); } }; // Vec<2> 特化 @@ -110,55 +103,56 @@ class Vec2 : public VecBase { public: Vec2() : VecBase() {} Vec2(real x, real y) : VecBase(x, y) {} - Vec2(const Vec2& v) : VecBase(v.x(), v.y()) {} + Vec2(const Vec2 &v) : VecBase(v.x(), v.y()) {} - Vec2 &operator=(const Vec2& v) { + Vec2 &operator=(const Vec2 &v) { data = v.data; return *this; } - real& x() { return data[0]; } - real& y() { return data[1]; } + real &x() { return data[0]; } + real &y() { return data[1]; } - const real& x() const { return data[0]; } - const real& y() const { return data[1]; } + const real &x() const { return data[0]; } + const real &y() const { return data[1]; } - real& u() { return data[0]; } - real& v() { return data[1]; } + real &u() { return data[0]; } + real &v() { return data[1]; } - const real& u() const { return data[0]; } - const real& v() const { return data[1]; } -}; + 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 { public: Vec3() : VecBase() {} Vec3(real x, real y, real z) : VecBase(x, y, z) {} - Vec3(const Vec3& v) : VecBase(v.x(), v.y(), v.z()) {} + Vec3(const Vec3 &v) : VecBase(v.x(), v.y(), v.z()) {} - Vec3 &operator=(const Vec3& v) { + Vec3 &operator=(const Vec3 &v) { data = v.data; return *this; } - real& x() { return data[0]; } - real& y() { return data[1]; } - real& z() { return data[2]; } + real &x() { return data[0]; } + real &y() { return data[1]; } + real &z() { return data[2]; } real x() const { return data[0]; } real y() const { return data[1]; } real z() const { return data[2]; } - real& u() { return data[0]; } - real& v() { return data[1]; } - real& w() { return data[2]; } + real &u() { return data[0]; } + real &v() { return data[1]; } + real &w() { return data[2]; } real u() const { return data[0]; } real v() const { return data[1]; } real w() const { return data[2]; } - Vec3 cross(const Vec3& v) const { + Vec3 cross(const Vec3 &v) const { return {y() * v.z() - z() * v.y(), z() * v.x() - x() * v.z(), x() * v.y() - y() * v.x()}; } }; @@ -166,6 +160,19 @@ public: using Pt3Array = std::vector; using Pt2Array = std::vector; +template +concept IsVec2 = requires(T t) { + t.x(); + t.y(); +}; + +template +concept IsVec3 = requires(T t) { + t.x(); + t.y(); + t.z(); +}; + //class Vec3 { //public: // real x, y, z; diff --git a/main.cpp b/main.cpp index 7643b28..69ea093 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,5 @@ #include -#include +#include "solid.hpp" // TIP To Run code, press or // click the icon in the gutter. diff --git a/require.md b/require.md index 4aab9ce..212725a 100644 --- a/require.md +++ b/require.md @@ -25,4 +25,56 @@ virtual CPmSolid * extrude(const CArray & polys, const PMSoft::CPMGeVector3D &extusionVector, // ? 没有路径吗 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 & polys, const CArray & 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); ``` \ No newline at end of file