diff --git a/example_brep/example_brep.vcxproj b/example_brep/example_brep.vcxproj
index c9f91ece..a2b825ce 100644
--- a/example_brep/example_brep.vcxproj
+++ b/example_brep/example_brep.vcxproj
@@ -22,32 +22,32 @@
{765B902B-4562-4035-8BBF-EBAB2A9602A3}
Win32Proj
example_brep
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_convert/example_convert.vcxproj b/example_convert/example_convert.vcxproj
index bf84115e..c70575ba 100644
--- a/example_convert/example_convert.vcxproj
+++ b/example_convert/example_convert.vcxproj
@@ -22,32 +22,32 @@
{15C98F21-2AC9-44C8-8752-25DAFA1C739F}
Win32Proj
example_convert
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_convert/example_convert.vcxproj.filters b/example_convert/example_convert.vcxproj.filters
index 7b21b6b2..1c46c9b6 100644
--- a/example_convert/example_convert.vcxproj.filters
+++ b/example_convert/example_convert.vcxproj.filters
@@ -10,7 +10,7 @@
-
+
Source Files
diff --git a/example_read/example_read.vcxproj b/example_read/example_read.vcxproj
index 639f6cca..7cdbd02d 100644
--- a/example_read/example_read.vcxproj
+++ b/example_read/example_read.vcxproj
@@ -22,32 +22,32 @@
{14A32F02-5A5D-49F7-9156-7EA3608C5900}
Win32Proj
example_read
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_roundtrip/example_roundtrip.vcxproj b/example_roundtrip/example_roundtrip.vcxproj
index 5e70411e..37a00d01 100644
--- a/example_roundtrip/example_roundtrip.vcxproj
+++ b/example_roundtrip/example_roundtrip.vcxproj
@@ -22,32 +22,32 @@
{0AFC8D30-5E7B-429D-82D2-F26868BF3CA6}
Win32Proj
example_roundtrip
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_test/example_test.vcxproj b/example_test/example_test.vcxproj
index 902b2161..7ca6b01c 100644
--- a/example_test/example_test.vcxproj
+++ b/example_test/example_test.vcxproj
@@ -23,31 +23,32 @@
{865E8D8D-8E03-4601-A7B5-A8C4094ECB8B}
Win32Proj
exampletest
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_userdata/example_userdata.vcxproj b/example_userdata/example_userdata.vcxproj
index 0f359217..645f3099 100644
--- a/example_userdata/example_userdata.vcxproj
+++ b/example_userdata/example_userdata.vcxproj
@@ -22,32 +22,32 @@
{F6FC693F-2EDB-4DEC-936A-C15BE1195EC4}
Win32Proj
example_userdata
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/example_write/example_write.vcxproj b/example_write/example_write.vcxproj
index b70b1642..95ebee2f 100644
--- a/example_write/example_write.vcxproj
+++ b/example_write/example_write.vcxproj
@@ -22,32 +22,32 @@
{75A90363-D54A-4C56-B4FC-900E7540331C}
Win32Proj
example_write
- 8.1
+ 10.0
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
Application
true
- v141
+ v142
Unicode
Application
false
- v141
+ v142
true
Unicode
diff --git a/freetype263/freetype263.vcxproj b/freetype263/freetype263.vcxproj
index 16211359..1c32a087 100644
--- a/freetype263/freetype263.vcxproj
+++ b/freetype263/freetype263.vcxproj
@@ -22,31 +22,32 @@
{426B0F99-1100-4CD6-9CB3-78C460817951}
Win32Proj
freetype263
+ 10.0
DynamicLibrary
true
- v141
+ v142
Unicode
DynamicLibrary
false
- v141
+ v142
true
Unicode
DynamicLibrary
true
- v141
+ v142
Unicode
DynamicLibrary
false
- v141
+ v142
true
Unicode
diff --git a/freetype263/freetype263_staticlib.vcxproj b/freetype263/freetype263_staticlib.vcxproj
index 8fb46aaf..c2a30b51 100644
--- a/freetype263/freetype263_staticlib.vcxproj
+++ b/freetype263/freetype263_staticlib.vcxproj
@@ -22,31 +22,32 @@
{F28EFCCD-948B-425C-B9FC-112D84A6498D}
Win32Proj
freetype263_staticlib
+ 10.0
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
true
Unicode
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
true
Unicode
diff --git a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj
index 7d7c312e..4d7ca33f 100644
--- a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj
+++ b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj
@@ -238,7 +238,7 @@
1DB028251ED6433600FA9144 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
+ LastUpgradeCheck = 1120;
ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit";
TargetAttributes = {
1DB0282C1ED6433600FA9144 = {
@@ -252,8 +252,8 @@
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
- English,
en,
+ Base,
);
mainGroup = 1DB028241ED6433600FA9144;
productRefGroup = 1DB0282E1ED6433600FA9144 /* Products */;
@@ -322,6 +322,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -378,6 +379,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
diff --git a/makefile b/makefile
index 3a48e468..3a4c457d 100644
--- a/makefile
+++ b/makefile
@@ -195,6 +195,7 @@ ON_INC = opennurbs.h \
opennurbs_sumsurface.h \
opennurbs_surface.h \
opennurbs_surfaceproxy.h \
+ opennurbs_symmetry.h \
opennurbs_system.h \
opennurbs_system_compiler.h \
opennurbs_system_runtime.h \
@@ -373,6 +374,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \
opennurbs_sumsurface.cpp \
opennurbs_surface.cpp \
opennurbs_surfaceproxy.cpp \
+ opennurbs_symmetry.cpp \
opennurbs_terminator.cpp \
opennurbs_text.cpp \
opennurbs_text_style.cpp \
@@ -549,6 +551,7 @@ ON_OBJ = opennurbs_3dm_attributes.o \
opennurbs_sumsurface.o \
opennurbs_surface.o \
opennurbs_surfaceproxy.o \
+ opennurbs_symmetry.o \
opennurbs_terminator.o \
opennurbs_text.o \
opennurbs_text_style.o \
diff --git a/opennurbs.h b/opennurbs.h
index e91c76f3..4c10917a 100644
--- a/opennurbs.h
+++ b/opennurbs.h
@@ -82,6 +82,7 @@
#include "opennurbs_line.h" // simple line
+#include "opennurbs_symmetry.h"
#include "opennurbs_polyline.h" // simple polyline
#include "opennurbs_cylinder.h" // simple 3d elliptical cylinder
#include "opennurbs_cone.h" // simple 3d right circular cone
diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp
index 7a197890..e55a78d9 100644
--- a/opennurbs_annotationbase.cpp
+++ b/opennurbs_annotationbase.cpp
@@ -1160,6 +1160,11 @@ bool ON_Annotation::EqualTextPositionProperties(
: text->EqualTextPositionProperties( Type(), dimstyle );
}
+static bool DimstyleHasMask(const ON_DimStyle* dimstyle)
+{
+ return (nullptr == dimstyle || dimstyle->DrawTextMask() || dimstyle->MaskFrameType() != ON_TextMask::MaskFrame::NoFrame);
+}
+
const ON_SHA1_Hash ON_Annotation::Internal_GetBBox_InputHash(
const ON_Viewport* vp,
const ON_DimStyle* dimstyle,
@@ -1185,7 +1190,12 @@ const ON_SHA1_Hash ON_Annotation::Internal_GetBBox_InputHash(
{
// This return ON_TextContent.m_mbox
// which is a cached value
- sha1.AccumulateBoundingBox(m_text->BoundingBox());
+ const ON_BoundingBox textbbox = m_text->BoundingBox();
+ sha1.AccumulateBoundingBox(textbbox);
+ if (textbbox.IsNotEmpty() && DimstyleHasMask(dimstyle))
+ {
+ sha1.AccumulateDouble(dimstyle->MaskBorder());
+ }
}
sha1.Accumulate2dPoint(text_point);
@@ -1206,12 +1216,27 @@ bool ON_Annotation::Internal_GetBBox_TextGlyphBox(
text_glyph_box = ON_BoundingBox::UnsetBoundingBox;
if (m_text)
{
+ text_glyph_box = m_text->BoundingBox();
+
+ // if mask, grow 2d bbox
+ if (text_glyph_box.IsNotEmpty() && DimstyleHasMask(dimstyle))
+ {
+ ON_3dPoint bmin = text_glyph_box.Min();
+ ON_3dPoint bmax = text_glyph_box.Max();
+ double d = dimstyle->MaskBorder();
+ bmin.x -= d;
+ bmin.y -= d;
+ bmax.x += d;
+ bmax.y += d;
+ text_glyph_box.m_min = bmin;
+ text_glyph_box.m_max = bmax;
+ }
+
ON_Xform txf;
// 20 June 2017 S. Baer (RH-39835)
// GetTextXform can change cached information. Make sure this
// is called before m_text->BoundingBox()
bool b = GetTextXform(vp, dimstyle, dimscale, txf);
- text_glyph_box = m_text->BoundingBox();
if (b)
{
text_glyph_box.Transform(txf);
@@ -2060,6 +2085,23 @@ void ON_Annotation::SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask
}
}
+ON_TextMask::MaskFrame ON_Annotation::MaskFrameType(const ON_DimStyle* parent_style) const
+{
+ return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskFrameType).MaskFrameType();
+}
+
+void ON_Annotation::SetMaskFrameType(const ON_DimStyle* parent_style, ON_TextMask::MaskFrame value)
+{
+ parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style);
+ bool bCreate = (value != parent_style->MaskFrameType());
+ ON_DimStyle* override_style = Internal_GetOverrideStyle(bCreate);
+ if (nullptr != override_style)
+ {
+ override_style->SetMaskFrameType(value);
+ override_style->SetFieldOverride(ON_DimStyle::field::MaskFrameType, bCreate);
+ }
+}
+
ON_Color ON_Annotation::MaskColor(const ON_DimStyle* parent_style) const
{
return Internal_StyleForFieldQuery(parent_style,ON_DimStyle::field::MaskColor).MaskColor();
@@ -2103,6 +2145,7 @@ void ON_Annotation::SetTextMask(const ON_DimStyle* parent_style, const ON_TextMa
SetMaskColor(parent_style, local_mask.MaskColor());
SetMaskFillType(parent_style, local_mask.MaskFillType());
SetMaskBorder(parent_style, local_mask.MaskBorder());
+ SetMaskFrameType(parent_style, local_mask.MaskFrameType());
}
double ON_Annotation::FixedExtensionLength(const ON_DimStyle* parent_style) const
@@ -3325,6 +3368,49 @@ const ON_Font* ON_Annotation::FirstCharFont() const
return &ON_Font::Default;
}
+bool ON_Annotation::IsAllBold() const
+{
+ return IsAllFormat(&ON_Font::IsBold);
+}
+
+bool ON_Annotation::IsAllItalic() const
+{
+ return IsAllFormat(&ON_Font::IsItalic);
+}
+
+bool ON_Annotation::IsAllUnderlined() const
+{
+ return IsAllFormat(&ON_Font::IsUnderlined);
+}
+
+bool ON_Annotation::IsAllFormat(bool (ON_Font::*func)() const) const
+{
+ if (nullptr == func)
+ return false;
+ const ON_TextContent* text = Text();
+ if (nullptr == text)
+ return false;
+
+ const ON_TextRunArray* runs = text->TextRuns(true);
+ if (nullptr == runs)
+ return false;
+
+ for (int i = 0; i < runs->Count(); i++)
+ {
+ const ON_TextRun* run = (*runs)[i];
+ if (nullptr == run)
+ continue;
+ ON_TextRun::RunType type = run->Type();
+ if (ON_TextRun::RunType::kText == type ||
+ ON_TextRun::RunType::kField == type ||
+ ON_TextRun::RunType::kFieldValue == type)
+ {
+ if (!(run->Font()->*func)())
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h
index c299d608..1fb38864 100644
--- a/opennurbs_annotationbase.h
+++ b/opennurbs_annotationbase.h
@@ -619,6 +619,10 @@ public:
ON_TextMask::MaskType MaskFillType(const ON_DimStyle* parent_style) const;
void SetMaskFillType(const ON_DimStyle* parent_style, ON_TextMask::MaskType source);
+ // Determines whether to draw a frame around a text mask
+ ON_TextMask::MaskFrame MaskFrameType(const ON_DimStyle* parent_style) const;
+ void SetMaskFrameType(const ON_DimStyle* parent_style, ON_TextMask::MaskFrame source);
+
ON_Color MaskColor(const ON_DimStyle* parent_style) const; // Only works right if MaskColorSource returns 1.
void SetMaskColor(const ON_DimStyle* parent_style, ON_Color color); // Does not return viewport background color
@@ -877,6 +881,19 @@ public:
const ON_Font* FirstCharFont() const;
+private:
+ bool IsAllFormat(bool (ON_Font::*func)() const) const;
+
+public:
+ // true if all of the text is bold
+ bool IsAllBold() const;
+
+ // true if all of the text is italic
+ bool IsAllItalic() const;
+
+ // true if all of the text is underlined
+ bool IsAllUnderlined() const;
+
friend class ON_Dimension;
};
diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h
index a77a4e2d..9d1ad077 100644
--- a/opennurbs_array_defs.h
+++ b/opennurbs_array_defs.h
@@ -799,6 +799,7 @@ bool ON_SimpleArray::QuickSortAndRemoveDuplicates( int (*compar)(const T*,con
continue; // duplicate
if (i > clean_count)
m_a[clean_count] = m_a[i];
+ prev_ele = &m_a[clean_count];
++clean_count;
}
if (clean_count < m_count)
diff --git a/opennurbs_curve.cpp b/opennurbs_curve.cpp
index 203fdb5e..9c73173b 100644
--- a/opennurbs_curve.cpp
+++ b/opennurbs_curve.cpp
@@ -2278,7 +2278,7 @@ ON_Curve* ON_TrimCurve(
else if ( trim_parameters.IsIncreasing() )
{
trimmed_curve = curve.DuplicateCurve();
- if( !trimmed_curve->Trim(trim_parameters) )
+ if(!trimmed_curve || !trimmed_curve->Trim(trim_parameters) )
{
delete trimmed_curve;
trimmed_curve = 0;
diff --git a/opennurbs_defines.h b/opennurbs_defines.h
index d61426d0..dbd83ca2 100644
--- a/opennurbs_defines.h
+++ b/opennurbs_defines.h
@@ -1976,6 +1976,34 @@ public:
//
/////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Rich text style
+ ///
+ /// The way rich text specifies fonts and other information depends on what
+ /// created the rich text. The interpretation of the rich text "specification"
+ /// varies widely and depends on the application, platform, and operating system.
+ ///
+ enum class RichTextStyle : unsigned char
+ {
+ /// Unset"
+ Unset = 0,
+
+ /// Rich text for use with the Windows 10 SDK. The font table uses Windows LOGFONT names.
+ Windows10SDK = 1,
+
+ /// Rich text for use with the Apple OS X SDK. The font table uses Postscript names.
+ AppleOSXSDK = 2,
+ };
+ static ON::RichTextStyle RichTextStyleFromUnsigned(unsigned int u);
+
+ /*
+ Returns:
+ ON::RichTextStyle::Windows10SDK on Windows and ON::RichTextStyle::AppleOSXSDK on OS X.
+ */
+ static ON::RichTextStyle RichTextStyleFromCurrentPlatform();
+
+
//// object_type ///////////////////////////////////////////////////
enum object_type
{
diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp
index 41f80718..5788aa83 100644
--- a/opennurbs_dimensionstyle.cpp
+++ b/opennurbs_dimensionstyle.cpp
@@ -321,6 +321,7 @@ ON_DimStyle::field ON_DimStyle::FieldFromUnsigned(
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextGap);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::TextHeight);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::DimTextLocation);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::MaskFrameType);
// OBSOLETE // //ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::OBSOLETE_LengthFormat_);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::LengthResolution);
ON_ENUM_FROM_UNSIGNED_CASE(ON_DimStyle::field::AngleFormat);
@@ -844,12 +845,14 @@ const ON_SHA1_Hash& ON_TextMask::ContentHash() const
if (m_content_hash.IsZeroDigest())
{
ON_SHA1 sha1;
- unsigned int u[2] = {
+ unsigned int u[3] = {
(m_bDrawMask ? 1U : 0U),
- (unsigned int)(static_cast(m_mask_type))
+ (unsigned int)(static_cast(m_mask_type)),
+ (unsigned int)(static_cast(m_mask_frame))
};
sha1.AccumulateUnsigned32(u[0]);
sha1.AccumulateUnsigned32(u[1]);
+ sha1.AccumulateUnsigned32(u[2]);
sha1.AccumulateUnsigned32(m_mask_color);
sha1.AccumulateDouble(m_mask_border);
m_content_hash = sha1.Hash();
@@ -910,6 +913,20 @@ void ON_TextMask::SetMaskFillType(ON_TextMask::MaskType type)
}
}
+ON_TextMask::MaskFrame ON_TextMask::MaskFrameType() const
+{
+ return m_mask_frame;
+}
+
+void ON_TextMask::SetMaskFrameType(ON_TextMask::MaskFrame frame)
+{
+ if (m_mask_frame != frame)
+ {
+ m_mask_frame = frame;
+ m_content_hash = ON_SHA1_Hash::ZeroDigest;
+ }
+}
+
ON_Color ON_TextMask::MaskColor() const
{
return m_mask_color;
@@ -951,12 +968,25 @@ ON_TextMask::MaskType ON_TextMask::MaskTypeFromUnsigned(
return ON_TextMask::MaskType::BackgroundColor;
}
+ON_TextMask::MaskFrame ON_TextMask::MaskFrameFromUnsigned(
+ unsigned int mask_frame_as_unsigned
+)
+{
+ switch (mask_frame_as_unsigned)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskFrame::NoFrame);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_TextMask::MaskFrame::RectFrame);
+ }
+ ON_ERROR("mask_type_as_unsigned parameter is not valid");
+ return ON_TextMask::MaskFrame::NoFrame;
+}
+
bool ON_TextMask::Write(
ON_BinaryArchive& archive
) const
{
- const int chunk_version = 0;
+ const int chunk_version = 1; // Oct. 9, 2019 - mask_frame
if (!archive.BeginWrite3dmAnonymousChunk(chunk_version))
return false;
@@ -976,6 +1006,11 @@ bool ON_TextMask::Write(
// DO NOT write m_content_hash
// END of chunk_version = 0 information
+ const unsigned int mask_frame_as_unsigned = (unsigned int)(static_cast(m_mask_frame));
+ if (!archive.WriteInt(mask_frame_as_unsigned))
+ break;
+ // END of chunk_version = 1 information
+
rc = true;
break;
}
@@ -1011,6 +1046,14 @@ bool ON_TextMask::Read(
break;
// END of chunk_version = 0 information
+ if (chunk_version > 0)
+ {
+ unsigned int mask_frame_as_unsigned = (unsigned int)(static_cast(m_mask_frame));
+ if (!archive.ReadInt(&mask_frame_as_unsigned))
+ break;
+ m_mask_frame = ON_TextMask::MaskFrameFromUnsigned(mask_frame_as_unsigned);
+ }
+
rc = true;
break;
}
@@ -5223,6 +5266,9 @@ void ON_DimStyle::OverrideFields(const ON_DimStyle& source, const ON_DimStyle& p
case ON_DimStyle::field::DimTextLocation:
ON_INTERNAL_UPDATE_PROPERTY(DimTextLocation);
break;
+ case ON_DimStyle::field::MaskFrameType:
+ ON_INTERNAL_UPDATE_PROPERTY(MaskFrameType);
+ break;
case ON_DimStyle::field::LengthResolution:
ON_INTERNAL_UPDATE_PROPERTY(LengthResolution);
break;
@@ -5687,6 +5733,7 @@ void ON_DimStyle::SetTextMask(const ON_TextMask& mask)
SetMaskColor(local_mask.MaskColor());
SetMaskFillType(local_mask.MaskFillType());
SetMaskBorder(local_mask.MaskBorder());
+ SetMaskFrameType(local_mask.MaskFrameType());
}
void ON_DimStyle::Internal_SetTextMask(
@@ -5747,6 +5794,29 @@ void ON_DimStyle::SetMaskFillType(ON_TextMask::MaskType source)
Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskColorSource);
}
+ON_TextMask::MaskFrame ON_DimStyle::MaskFrameType() const
+{
+ // This function is for legacy compatibility.
+ // In October 2016, text mask information was moved from
+ // a collection of individual values on ON_DimStyle to
+ // an ON_TextMask class and a single ON_TextMask m_text_mask member
+ // on ON_DimStyle.
+ return TextMask().MaskFrameType();
+}
+
+void ON_DimStyle::SetMaskFrameType(ON_TextMask::MaskFrame source)
+{
+ // This function is for legacy compatibility.
+ // In October 2016, text mask information was moved from
+ // a collection of individual values on ON_DimStyle to
+ // an ON_TextMask class and a single ON_TextMask m_text_mask member
+ // on ON_DimStyle.
+ ON_TextMask text_mask = TextMask();
+ text_mask.SetMaskFrameType(source);
+ Internal_SetTextMask(text_mask);
+ Internal_SetOverrideDimStyleCandidateFieldOverride(ON_DimStyle::field::MaskFrameType);
+}
+
ON_Color ON_DimStyle::MaskColor() const
{
// This function is for legacy compatibility.
diff --git a/opennurbs_dimensionstyle.h b/opennurbs_dimensionstyle.h
index ed596bd1..96a2c46d 100644
--- a/opennurbs_dimensionstyle.h
+++ b/opennurbs_dimensionstyle.h
@@ -114,7 +114,28 @@ public:
#pragma endregion
static ON_TextMask::MaskType MaskTypeFromUnsigned(
- unsigned int mask_type_as_unsigned
+ unsigned int mask_border_as_unsigned
+ );
+
+#pragma region RH_C_SHARED_ENUM [ON_TextMask::MaskFrame] [Rhino.DocObjects.DimensionStyle.MaskFrame] [nested:byte]
+ ///
+ /// Draw a frame stroke around the text mask area
+ ///
+ enum class MaskFrame : unsigned char
+ {
+ ///
+ /// Text mask frame not drawn
+ ///
+ NoFrame = 0,
+ ///
+ /// Text mask frame outline rectangle drawn
+ ///
+ RectFrame = 1,
+ };
+#pragma endregion
+
+ static ON_TextMask::MaskFrame MaskFrameFromUnsigned(
+ unsigned int mask_frame_as_unsigned
);
public:
@@ -156,6 +177,10 @@ public:
// Can be background color or a specific color
ON_TextMask::MaskType MaskFillType() const;
void SetMaskFillType(ON_TextMask::MaskType source);
+
+ // Determines whether or not to draw a rectangular frame around a text mask
+ ON_TextMask::MaskFrame MaskFrameType() const;
+ void SetMaskFrameType(ON_TextMask::MaskFrame frame);
/*
Returns:
@@ -196,8 +221,8 @@ public:
private:
bool m_bDrawMask = false;
ON_TextMask::MaskType m_mask_type = ON_TextMask::MaskType::BackgroundColor;
-
- unsigned char m_reserved1 = 0;
+ ON_TextMask::MaskFrame m_mask_frame = ON_TextMask::MaskFrame::NoFrame;
+
unsigned char m_reserved2 = 0;
ON_Color m_mask_color = ON_Color::White;
@@ -630,6 +655,8 @@ public:
DimTextLocation = 10,
//OBSOLETE_LengthFormat_ = 11,
+ /// Text mask frame
+ MaskFrameType = 11,
///
LengthResolution = 12,
@@ -1722,6 +1749,10 @@ public:
ON_TextMask::MaskType MaskFillType() const;
void SetMaskFillType(ON_TextMask::MaskType source);
+ // Determines whether to draw a frame around a Text Mask
+ ON_TextMask::MaskFrame MaskFrameType() const;
+ void SetMaskFrameType(ON_TextMask::MaskFrame source);
+
ON_Color MaskColor() const; // Only works right if MaskColorSource returns 1.
// Does not return viewport background color
void SetMaskColor(ON_Color color);
diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp
index 8c7902d1..d77a189f 100644
--- a/opennurbs_font.cpp
+++ b/opennurbs_font.cpp
@@ -1632,6 +1632,110 @@ int ON_FontFaceQuartet::CompareQuartetName(
return ON_wString::CompareOrdinal(lhs->m_quartet_name, rhs->m_quartet_name, true);
}
+ON::RichTextStyle ON::RichTextStyleFromUnsigned(unsigned int u)
+{
+ switch (u)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::Unset);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::Windows10SDK);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON::RichTextStyle::AppleOSXSDK);
+ };
+ ON_ERROR("Invalid ON::RichTextStyle value.");
+ return ON::RichTextStyle::Unset;
+}
+
+ON::RichTextStyle ON::RichTextStyleFromCurrentPlatform()
+{
+ return
+#if defined(ON_RUNTIME_WIN)
+ ON::RichTextStyle::Windows10SDK
+#elif defined(ON_RUNTIME_APPLE)
+ ON::RichTextStyle::AppleOSXSDK
+#else
+ ON::RichTextStyle::Unset
+#endif
+ ;
+}
+
+const ON_wString ON_FontFaceQuartet::RichTextSample(
+ ON::RichTextStyle rich_text_style
+) const
+{
+ const wchar_t* quartet_name = static_cast(m_quartet_name);
+ if (nullptr == quartet_name)
+ return ON_wString::EmptyString;
+ if ( nullptr == m_regular && nullptr == m_bold && nullptr == m_italic && nullptr == m_bold_italic)
+ return ON_wString::EmptyString;
+
+ const ON_wString regular_ps = (nullptr != m_regular) ? m_regular->PostScriptName() : ON_wString::EmptyString;
+ const ON_wString bold_ps = (nullptr != m_bold) ? m_bold->PostScriptName() : ON_wString::EmptyString;
+ const ON_wString italic_ps = (nullptr != m_italic) ? m_italic->PostScriptName() : ON_wString::EmptyString;
+ const ON_wString bold_italic_ps = (nullptr != m_bold_italic) ? m_bold_italic->PostScriptName() : ON_wString::EmptyString;
+
+ const ON_wString not_available(L"Not available.");
+ const ON_wString regular = regular_ps.IsNotEmpty() ? regular_ps : not_available;
+ const ON_wString bold = bold_ps.IsNotEmpty() ? bold_ps : not_available;
+ const ON_wString italic = italic_ps.IsNotEmpty() ? italic_ps : not_available;
+ const ON_wString bold_italic = bold_italic_ps.IsNotEmpty() ? bold_italic_ps : not_available;
+
+ ON_wString sample;
+ switch (rich_text_style)
+ {
+ case ON::RichTextStyle::Unset:
+ break;
+
+ case ON::RichTextStyle::Windows10SDK:
+ // font table uses Windows LOGFONT name and \b \i to select faces
+ sample = ON_wString::FormatToString(L"{\\rtf1\\deff0{\\fonttbl{\\f0 %ls;}}\\fs40", quartet_name);
+ sample += ON_wString::FormatToString(L"{\\f0 Windows 10 LOGFONT Quartet: %ls}{\\par}", quartet_name);
+ if (nullptr != m_regular)
+ sample += ON_wString::FormatToString(L"{\\f0 Regular: %ls}{\\par}", static_cast(regular_ps));
+ if (nullptr != m_bold)
+ sample += ON_wString::FormatToString(L"{\\f0\\b Bold: %ls}{\\par}", static_cast(bold_ps));
+ if (nullptr != m_italic)
+ sample += ON_wString::FormatToString(L"{\\f0\\i Italic: %ls}{\\par}", static_cast(italic_ps));
+ if (nullptr != m_bold_italic)
+ sample += ON_wString::FormatToString(L"{\\f0\\b\\i Bold Italic: %ls}{\\par}", static_cast(bold_italic_ps));
+ sample += ON_wString(L"\\par}");
+ break;
+
+ case ON::RichTextStyle::AppleOSXSDK:
+ // font table uses unique PostScript names for each face
+ if (regular_ps.IsNotEmpty() || bold_ps.IsNotEmpty() || italic_ps.IsNotEmpty() || bold_italic_ps.IsNotEmpty())
+ {
+ sample = ON_wString::FormatToString(L"{\\rtf1\\deff0{\\fonttbl");
+ int fdex = 0;
+ if (regular_ps.IsNotEmpty())
+ sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(regular_ps));
+ if (bold_ps.IsNotEmpty())
+ sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(bold_ps));
+ if (italic_ps.IsNotEmpty())
+ sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(italic_ps));
+ if (bold_italic_ps.IsNotEmpty())
+ sample += ON_wString::FormatToString(L"{\\f%d %ls;}", fdex++, static_cast(bold_italic_ps));
+ sample += ON_wString(L"}\\fs40");
+
+ sample += ON_wString::FormatToString(L"{\\f0 Apple OS X Fake Quartet: %ls}{\\par}", quartet_name);
+ fdex = 0;
+ if (nullptr != m_regular)
+ sample += ON_wString::FormatToString(L"{\\f%d Regular: %ls}{\\par}", fdex++, static_cast(regular_ps));
+ if (nullptr != m_bold)
+ sample += ON_wString::FormatToString(L"{\\f%d\\b Bold: %ls}{\\par}", fdex++, static_cast(bold_ps));
+ if (nullptr != m_italic)
+ sample += ON_wString::FormatToString(L"{\\f%d\\i Italic: %ls}{\\par}", fdex++, static_cast(italic_ps));
+ if (nullptr != m_bold_italic)
+ sample += ON_wString::FormatToString(L"{\\f%d\\b\\i Bold Italic: %ls}{\\par}", fdex++, static_cast(bold_italic_ps));
+ sample += ON_wString(L"\\par}");
+ }
+ break;
+
+ default:
+ break;
+ }
+ return sample;
+}
+
+
bool ON_FontFaceQuartet::HasRegularFace() const
{
return (nullptr != RegularFace());
@@ -6504,8 +6608,29 @@ const ON_wString ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames(
Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusMedium", L"Damascus Medium", ON_FontFaceQuartet::Member::Regular),
Internal_FakeWindowsLogfontName(L"Damascus", L"DamascusSemiBold", L"Damascus Semibold", ON_FontFaceQuartet::Member::Regular),
- Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedMedium", L"Futura Condensed", ON_FontFaceQuartet::Member::Regular),
- Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedExtraBold", L"Futura Condensed", ON_FontFaceQuartet::Member::Bold),
+ // DINPro https://mcneel.myjetbrains.com/youtrack/issue/RH-54627
+ // DO NOT try making a fake quartet that combines regular/bold.
+ // The last parameter for all DINPro "fake quartets" must be ON_FontFaceQuartet::Member::Regular.
+ // When text is created and then later edited the, the Mac RTF used by Eto and the "composed" RTF used by Rhino
+ // are not currently capable looking at the "fake quartet" information and preserving the font. So the Mac users get
+ // to see 5 "fake quartets" for DINPro. The "fix" is to return to the Rhino 5 Mac font UI.
+ Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Regular", L"DINPro Regular", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Bold", L"DINPro Bold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Light", L"DINPro Light", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Medium", L"DINPro Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"DINPro", L"DINPro-Black", L"DINPro Black", ON_FontFaceQuartet::Member::Regular),
+
+ // Futura https://mcneel.myjetbrains.com/youtrack/issue/RH-56085
+ // DO NOT try making a fake quartet that combines regular/bold.
+ // The last parameter for all Futura "fake quartets" must be ON_FontFaceQuartet::Member::Regular.
+ // When text is created and then later edited the, the Mac RTF used by Eto and the "composed" RTF used by Rhino
+ // are not currently capable looking at the "fake quartet" information and preserving the font. So the Mac users get
+ // to see 5 "fake quartets" for Futura. The "fix" is to return to the Rhino 5 Mac font UI.
+ Internal_FakeWindowsLogfontName(L"Futura", L"Futura-Medium", L"Futura Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Futura", L"Futura-Bold", L"Futura Bold", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Futura", L"Futura-MediumItalic", L"Futura Medium Italic", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedMedium", L"Futura Condensed Medium", ON_FontFaceQuartet::Member::Regular),
+ Internal_FakeWindowsLogfontName(L"Futura", L"Futura-CondensedExtraBold", L"Futura Condensed ExtraBold", ON_FontFaceQuartet::Member::Regular),
Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-Light", L"Gill Sans Light", ON_FontFaceQuartet::Member::Regular),
Internal_FakeWindowsLogfontName(L"Gill Sans", L"GillSans-LightItalic", L"Gill Sans Light", ON_FontFaceQuartet::Member::Italic),
diff --git a/opennurbs_font.h b/opennurbs_font.h
index 4b47a4a5..cd6e65f3 100644
--- a/opennurbs_font.h
+++ b/opennurbs_font.h
@@ -2180,6 +2180,13 @@ public:
const ON_FontFaceQuartet* rhs
);
+ /*
+ Returns a sample rich text string demonstrating the faces in the quartet.
+ */
+ const ON_wString RichTextSample(
+ ON::RichTextStyle rich_text_style
+ ) const;
+
public:
static const ON_FontFaceQuartet Empty;
diff --git a/opennurbs_glyph_outline.cpp b/opennurbs_glyph_outline.cpp
index b7ec56f3..e764c2c8 100644
--- a/opennurbs_glyph_outline.cpp
+++ b/opennurbs_glyph_outline.cpp
@@ -619,26 +619,23 @@ const ON_BoundingBox ON_OutlineFigure::BoundingBox() const
ON_2fPoint bbox_cv_min = bbox_min;
ON_2fPoint bbox_cv_max = bbox_max;
- for (ON__UINT32 i = 0; i <= figure_end_dex; i++)
+ for (ON__UINT32 i = 0U; i < figure_end_dex; ++i)
{
const ON__UINT32 degree = ON_OutlineFigure::Internal_SegmentDegree(i);
- if (0 == degree)
+ if (degree < 1U || degree > 3U)
continue;
-
- ON_OutlineFigurePoint p = a[i];
- if (false == p.IsOnFigure())
- continue;
+ // a[i] = start of bezier (line,quadratic,or cubic)
+ ON_OutlineFigurePoint p = a[i+degree]; // p = a[i+degree] = end of bezier (line,quadratic,or cubic)
Internal_GrowBBox(p.m_point, p.m_point, bbox_min, bbox_max);
- if (degree < 2)
- continue;
-
- p = a[++i];
- Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max);
-
- if (3 == degree)
+ if (degree >= 2U)
{
- p = a[++i];
+ p = a[++i]; // p = 1st interior cv of a quadratic or cubic bezier
Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max);
+ if (3U == degree)
+ {
+ p = a[++i]; // p = 2nd interior cv of a cubic bezier
+ Internal_GrowBBox(p.m_point, p.m_point, bbox_cv_min, bbox_cv_max);
+ }
}
}
@@ -660,21 +657,21 @@ const ON_BoundingBox ON_OutlineFigure::BoundingBox() const
ON_OutlineFigurePoint cv[4];
cv[3] = a[0]; // to suppress potintial uninitialized memory warning
- for (ON__UINT32 i = 0; i <= figure_end_dex; i++)
+ for (ON__UINT32 i = 0U; i < figure_end_dex; ++i)
{
const ON__UINT32 degree = ON_OutlineFigure::Internal_SegmentDegree(i);
- if (degree < 1 || degree > 3)
- continue;
- cv[0] = a[i++];
- cv[1] = a[i];
- cv[2] = a[i+1];
+ if (degree < 2U || degree > 3U)
+ continue; // correct to continue when degree = 1
+ cv[0] = a[i]; // [a[i] = start of quadratic or cubic bezier
+ cv[1] = a[++i]; // 1st interior cv of a quadratic or cubic bezer
bbox_cv_min = cv[1].m_point;
bbox_cv_max = bbox_cv_min;
- if (3 == degree)
+ cv[2] = a[i + 1]; // end of quadratic bezier or penultimate cv of a cubic bezier
+ if (3U == degree)
{
Internal_GrowBBox(cv[2].m_point, cv[2].m_point, bbox_cv_min, bbox_cv_max);
- i++;
- cv[3] = a[i+1];
+ ++i;
+ cv[3] = a[i + 1]; // end of a cubic bezier
}
if (
diff --git a/opennurbs_hash_table.cpp b/opennurbs_hash_table.cpp
index f9ed79d3..e6fada60 100644
--- a/opennurbs_hash_table.cpp
+++ b/opennurbs_hash_table.cpp
@@ -41,6 +41,16 @@ ON__UINT32 ON_Hash32TableItem::HashTableSerialNumber() const
return m_internal_hash_table_sn;
}
+ON__UINT32 ON_Hash32TableItem::HashTableItemHash() const
+{
+ return m_internal_hash32;
+}
+
+void ON_Hash32TableItem::ClearHashTableSerialNumberForExperts()
+{
+ m_internal_hash_table_sn = 0;
+}
+
//#define ON_DEBUG_ON_Hash32Table_ValidateEachTransaction
#if defined(ON_DEBUG_ON_Hash32Table_ValidateEachTransaction)
diff --git a/opennurbs_hash_table.h b/opennurbs_hash_table.h
index 75650886..d6a736be 100644
--- a/opennurbs_hash_table.h
+++ b/opennurbs_hash_table.h
@@ -41,6 +41,37 @@ public:
const ON_UUID& id
);
+ /*
+ Returns:
+ If this item has been added to an ON_Hash32Table.AddItem(hash32,item pointer) then the
+ value of hash3d passed as the first argument to ON_Hash32Table.AddItem(hash32,item pointer)
+ is returned. This is the value the ON_Hash32Table uses for this item.
+ Othewise 0 is returned.
+ Remarks:
+ This function is useful when copying hash tables.
+
+ count = src_hash_table.ItemCount();
+ MyHashTableItems src_items[count]; // items added to src_hash_table
+
+ // copy src_hash_table
+ MyHashTableItems copied_items[count];
+ copied_items = src_items;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ ON_SubDSurfaceInterpolatortHash32TableItem& hitem = copied_items[i];
+ hitem.ClearHashTableSerialNumberForExperts();
+ m_htable.AddItem(hitem.HashTableItemHash(), &hitem);
+ }
+ */
+ ON__UINT32 HashTableItemHash() const;
+
+ /*
+ Description:
+ Useful when copying hash tables to remove the hash table reference from
+ a copied hash item. Never remove the hash table reference from an item
+ that is still in a hash table.
+ */
+ void ClearHashTableSerialNumberForExperts();
private:
friend class ON_Hash32Table;
mutable ON_Hash32TableItem* m_internal_next = nullptr;
diff --git a/opennurbs_line.cpp b/opennurbs_line.cpp
index 5e3d776b..1b9faea7 100644
--- a/opennurbs_line.cpp
+++ b/opennurbs_line.cpp
@@ -95,7 +95,10 @@ double ON_Line::Length() const
ON_3dVector ON_Line::Direction() const
{
- return (to-from);
+ return (ON_UNSET_VALUE < to.x && to.x < ON_UNSET_POSITIVE_VALUE && ON_UNSET_VALUE GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
+ return nullptr != pUD;
+ }
+
+ void RemoveUserData(void) const
+ {
+ auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
+
+ if (material->DetachUserData(pUD))
+ {
+ delete pUD;
+ }
+ }
+
const ON_PhysicallyBasedMaterialUserData& UserData() const
{
const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
@@ -6944,19 +6960,22 @@ void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d)
}
ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src)
- : _pImpl(new Impl(const_cast(src)))
{
+ //Placement new - create the impl in the stack allocated space.
+ new (&_impl) Impl(const_cast(src));
}
ON_PhysicallyBasedMaterial::~ON_PhysicallyBasedMaterial()
{
- delete _pImpl;
+ //delete _pImpl;
+ //Call the dtor, don't release the memory
+ Implementation().~Impl();
}
ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src)
- : _pImpl(new Impl(*src._pImpl->material))
{
+ new (&_impl) Impl(*src.Implementation().material);
}
ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void)
@@ -6972,20 +6991,32 @@ const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const
const ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) const
{
- return *_pImpl;
+ return *reinterpret_cast(_impl);
}
ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void)
{
- return *_pImpl;
+ return *reinterpret_cast(_impl);
}
bool ON_PhysicallyBasedMaterial::Supported(void) const
{
+ if (!Implementation().UserDataExists())
+ return false;
+
return BaseColor().IsValid();
}
+void ON_PhysicallyBasedMaterial::Destroy(void)
+{
+ ON_Material& mat = *Implementation().material;
+ Implementation().~Impl();
+ new (&_impl) Impl(const_cast(mat));
+
+ Implementation().RemoveUserData();
+}
+
void ON_PhysicallyBasedMaterial::SynchronizeLegacyMaterial(void)
{
auto& mat = *Implementation().material;
diff --git a/opennurbs_material.h b/opennurbs_material.h
index 8ad7e28c..ead2871a 100644
--- a/opennurbs_material.h
+++ b/opennurbs_material.h
@@ -673,50 +673,54 @@ public:
virtual void SetEmission(ON_4fColor);
//Texture access functions - exactly the same as ON_Material. Provided for ease of use.
- int FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0 = -1) const;
- int AddTexture(const ON_Texture& tx);
- int AddTexture( const wchar_t* filename, ON_Texture::TYPE type);
- int DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type);
+ virtual int FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0 = -1) const;
+ virtual int AddTexture(const ON_Texture& tx);
+ virtual int AddTexture( const wchar_t* filename, ON_Texture::TYPE type);
+ virtual int DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type);
//Access the referenced ON_Material.
- ON_Material& Material(void);
- const ON_Material& Material(void) const;
+ virtual ON_Material& Material(void);
+ virtual const ON_Material& Material(void) const;
//Call this function to set the ON_Material up to represent the PBR material as well as possible.
- void SynchronizeLegacyMaterial(void);
+ virtual void SynchronizeLegacyMaterial(void);
+
+ //Expert function to remove all PBR data from a material
+ virtual void Destroy(void);
public:
- ON_DEPRECATED class ON_CLASS ParametersNames
+ class ON_CLASS ParametersNames
{
public:
- ON_DEPRECATED static ON_wString BaseColor(void);
- ON_DEPRECATED static ON_wString BRDF(void);
- ON_DEPRECATED static ON_wString Subsurface(void);
- ON_DEPRECATED static ON_wString SubsurfaceScatteringColor(void);
- ON_DEPRECATED static ON_wString SubsurfaceScatteringRadius(void);
- ON_DEPRECATED static ON_wString Specular(void);
- ON_DEPRECATED static ON_wString SpecularTint(void);
- ON_DEPRECATED static ON_wString Metallic(void);
- ON_DEPRECATED static ON_wString Roughness(void);
- ON_DEPRECATED static ON_wString Anisotropic(void);
- ON_DEPRECATED static ON_wString AnisotropicRotation(void);
- ON_DEPRECATED static ON_wString Sheen(void);
- ON_DEPRECATED static ON_wString SheenTint(void);
- ON_DEPRECATED static ON_wString Clearcoat(void);
- ON_DEPRECATED static ON_wString ClearcoatRoughness(void);
- ON_DEPRECATED static ON_wString ClearcoatBump(void);
- ON_DEPRECATED static ON_wString OpacityIor(void);
- ON_DEPRECATED static ON_wString Opacity(void);
- ON_DEPRECATED static ON_wString OpacityRoughness(void);
- ON_DEPRECATED static ON_wString Emission(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString BaseColor(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString BRDF(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Subsurface(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SubsurfaceScatteringColor(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SubsurfaceScatteringRadius(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Specular(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SpecularTint(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Metallic(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Roughness(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Anisotropic(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString AnisotropicRotation(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Sheen(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString SheenTint(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Clearcoat(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString ClearcoatRoughness(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString ClearcoatBump(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString OpacityIor(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Opacity(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString OpacityRoughness(void);
+ ON_DEPRECATED_MSG("Use CRhRdkMaterial::PhysicallyBased::ParameterNames") static ON_wString Emission(void);
};
private:
class Impl;
- Impl* _pImpl;
const Impl& Implementation(void) const;
Impl& Implementation(void);
+ unsigned char _impl[64];
+
//Ban copying - usage should be material.PhysicallyBased().Function()
ON_PhysicallyBasedMaterial& operator=(const ON_Material& src) = delete;
ON_PhysicallyBasedMaterial& operator=(const ON_PhysicallyBasedMaterial& src) = delete;
diff --git a/opennurbs_mesh.cpp b/opennurbs_mesh.cpp
index 10b80ed7..60008a15 100644
--- a/opennurbs_mesh.cpp
+++ b/opennurbs_mesh.cpp
@@ -2726,7 +2726,25 @@ bool ON_Mesh::Transform(
if (false == bIsValid_fV)
m_V.SetCount(0);
- const bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
+ bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
+
+ // 2 Jan 2020 S. Baer (RH-54464)
+ // If the transform is moving the mesh into or out of "beyond single precision",
+ // set up double precision vertices in order to get our best possible precision
+ // after the transform.
+ if (false == bIsValid_dV)
+ {
+ ON_BoundingBox bbox = BoundingBox();
+ ON_BoundingBox transformed_bbox = bbox;
+ transformed_bbox.Transform(xform);
+ if (ON_BeyondSinglePrecision(bbox, nullptr) || ON_BeyondSinglePrecision(transformed_bbox, nullptr))
+ {
+ UpdateDoublePrecisionVertices();
+ bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
+ }
+ }
+
+
if (false == bIsValid_dV)
m_dV.SetCount(0);
diff --git a/opennurbs_nurbssurface.cpp b/opennurbs_nurbssurface.cpp
index 74fbd870..1b49df49 100644
--- a/opennurbs_nurbssurface.cpp
+++ b/opennurbs_nurbssurface.cpp
@@ -3304,3 +3304,432 @@ bool ON_NurbsSurface::ConvertSpanToBezier(
}
return true;
}
+
+static bool ValidateHermiteData(
+ const ON_SimpleArray& u_Parameters,
+ const ON_SimpleArray& v_Parameters,
+ const ON_ClassArray>& GridPoints,
+ const ON_ClassArray>& u_Tangents,
+ const ON_ClassArray>& v_Tangents,
+ const ON_ClassArray>& TwistVectors)
+{
+ int n = u_Parameters.Count();
+ int m = v_Parameters.Count();
+ if (n < 2 || m < 2)
+ return false;
+
+ for (int i = 0; i < u_Parameters.Count() - 1; i++)
+ if (u_Parameters[i] >= u_Parameters[i + 1])
+ return false;
+
+ for (int j = 0; j < v_Parameters.Count() - 1; j++)
+ if (v_Parameters[j] >= v_Parameters[j + 1])
+ return false;
+
+ if (GridPoints.Count() != n)
+ return false;
+ for (int i = 0; i < GridPoints.Count(); i++)
+ if (GridPoints[i].Count() != m)
+ return false;
+
+ if (u_Tangents.Count() != n)
+ return false;
+ for (int i = 0; i < u_Tangents.Count(); i++)
+ if (u_Tangents[i].Count() != m)
+ return false;
+
+ if (v_Tangents.Count() != n)
+ return false;
+ for (int i = 0; i < v_Tangents.Count(); i++)
+ if (v_Tangents[i].Count() != m)
+ return false;
+
+ if (TwistVectors.Count() != n)
+ return false;
+ for (int i = 0; i < TwistVectors.Count(); i++)
+ if (TwistVectors[i].Count() != m)
+ return false;
+
+ return true;
+}
+
+class ON_NurbsSurface* ON_NurbsSurface::CreateHermiteSurface(
+ const ON_SimpleArray& u,
+ const ON_SimpleArray& v,
+ const ON_ClassArray>& GridPoints,
+ const ON_ClassArray>& u_Tan,
+ const ON_ClassArray>& v_Tan,
+ const ON_ClassArray>& Twist,
+ class ON_NurbsSurface* hsrf )
+{
+ if (!ValidateHermiteData( u, v, GridPoints, u_Tan, v_Tan, Twist ))
+ return nullptr;
+
+ int n = u.Count();
+ int m = v.Count();
+
+ if (hsrf == nullptr)
+ {
+ hsrf = ON_NurbsSurface::New();
+ }
+
+ bool rc = hsrf->Create(3, false, 4, 4, 3 * n - 2, 3 * m - 2);
+ if (rc)
+ {
+ // Set the knots
+ for (int i = 0; i < n; i++)
+ {
+ hsrf->SetKnot(0, 3 * i, u[i]);
+ hsrf->SetKnot(0, 3 * i + 1, u[i]);
+ hsrf->SetKnot(0, 3 * i + 2, u[i]);
+ }
+ for (int j = 0; j < m; j++)
+ {
+ hsrf->SetKnot(1, 3 * j, v[j]);
+ hsrf->SetKnot(1, 3 * j + 1, v[j]);
+ hsrf->SetKnot(1, 3 * j + 2, v[j]);
+ }
+
+ // Set GridPoints
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < m ; j++)
+ hsrf->SetCV(3*i, 3*j, GridPoints[i][j]);
+
+ // set the points on v - isos between grid points
+ for (int j = 0; j < m-1; j++)
+ for (int i = 0; i < n; i++)
+ {
+ double delv = 1.0/3.0 * (v[j + 1] - v[j]);
+ hsrf->SetCV(3 * i, 3 * j + 1, GridPoints[i][j] + delv * v_Tan[i][j]);
+ hsrf->SetCV(3 * i, 3 * j + 2, GridPoints[i][j+1] - delv * v_Tan[i][j+1]);
+ }
+
+ // set the points on u - isos between grid points
+ for (int i = 0; i < n - 1; i++)
+ for (int j= 0; j < m; j++)
+ {
+ double delu = 1.0/3.0 * (u[i + 1] - u[i]);
+ hsrf->SetCV(3 * i + 1, 3 * j, GridPoints[i][j] + delu * u_Tan[i][j]);
+ hsrf->SetCV(3 * i + 2, 3 * j, GridPoints[i+1][j] - delu * u_Tan[i+1][j]);
+ }
+
+ // set the interior points off the grid iso's
+ for( int i=0; iSetCV(3 * i + 1, 3 * j + 1, GridPoints[i][j] + ( delu * u_Tan[i][j] + delv * v_Tan[i][j]) + deluv * Twist[i][j]);
+ hsrf->SetCV(3 * i + 2, 3 * j + 1, GridPoints[i+1][j] + (-delu * u_Tan[i+1][j] + delv * v_Tan[i+1][j]) - deluv * Twist[i+1][j]);
+ hsrf->SetCV(3 * i + 1, 3 * j + 2, GridPoints[i][j+1] + ( delu * u_Tan[i][j+1] - delv * v_Tan[i][j+1]) - deluv * Twist[i][j+1]);
+ hsrf->SetCV(3 * i + 2, 3 * j + 2, GridPoints[i+1][j+1]+ (-delu * u_Tan[i+1][j+1]- delv * v_Tan[i+1][j+1])+ deluv * Twist[i+1][j+1]);
+ }
+ }
+ if (!rc)
+ hsrf = nullptr;
+ return hsrf;
+}
+
+
+ON_HermiteSurface::ON_HermiteSurface()
+ : m_u_count(0)
+ , m_v_count(0)
+{
+}
+
+ON_HermiteSurface::ON_HermiteSurface(int u_count, int v_count)
+ : m_u_count(u_count)
+ , m_v_count(v_count)
+{
+ Create(u_count, v_count);
+}
+
+ON_HermiteSurface::~ON_HermiteSurface()
+{
+ Destroy();
+}
+
+bool ON_HermiteSurface::Create(int u_count, int v_count)
+{
+ Destroy();
+
+ if (u_count < 2 || v_count < 2)
+ return false;
+
+ m_u_count = u_count;
+ m_v_count = v_count;
+
+ m_u_parameters.SetCapacity(m_u_count);
+ m_u_parameters.SetCount(m_u_count);
+ for (int i = 0; i < m_u_count; i++)
+ m_u_parameters[i] = ON_UNSET_VALUE;
+
+ m_v_parameters.SetCapacity(m_v_count);
+ m_v_parameters.SetCount(m_v_count);
+ for (int i = 0; i < m_v_count; i++)
+ m_v_parameters[i] = ON_UNSET_VALUE;
+
+ m_grid_points.SetCapacity(m_v_count);
+ for (int i = 0; i < m_v_count; i++)
+ {
+ ON_SimpleArray& arr = m_grid_points.AppendNew();
+ arr.SetCapacity(m_u_count);
+ arr.SetCount(m_u_count);
+ arr.Zero();
+ }
+
+ m_u_tangents.SetCapacity(m_v_count);
+ for (int i = 0; i < m_v_count; i++)
+ {
+ ON_SimpleArray& arr = m_u_tangents.AppendNew();
+ arr.SetCapacity(m_u_count);
+ arr.SetCount(m_u_count);
+ for (int j = 0; j < m_u_count; j++)
+ arr[j] = ON_3dPoint::UnsetPoint;
+ }
+
+ m_v_tangents.SetCapacity(m_v_count);
+ for (int i = 0; i < m_v_count; i++)
+ {
+ ON_SimpleArray& arr = m_v_tangents.AppendNew();
+ arr.SetCapacity(m_u_count);
+ arr.SetCount(m_u_count);
+ for (int j = 0; j < m_u_count; j++)
+ arr[j] = ON_3dVector::UnsetVector;
+ }
+
+ m_twists.SetCapacity(m_v_count);
+ for (int i = 0; i < m_v_count; i++)
+ {
+ ON_SimpleArray& arr = m_twists.AppendNew();
+ arr.SetCapacity(m_u_count);
+ arr.SetCount(m_u_count);
+ for (int j = 0; j < m_u_count; j++)
+ arr[j] = ON_3dVector::UnsetVector;
+ }
+
+ return true;
+}
+
+void ON_HermiteSurface::Destroy()
+{
+ m_u_parameters.Destroy();
+ m_v_parameters.Destroy();
+
+ for (int i = 0; i < m_grid_points.Count(); i++)
+ m_grid_points[i].Destroy();
+ m_grid_points.Destroy();
+
+ for (int i = 0; i < m_u_tangents.Count(); i++)
+ m_u_tangents[i].Destroy();
+ m_u_tangents.Destroy();
+
+ for (int i = 0; i < m_v_tangents.Count(); i++)
+ m_v_tangents[i].Destroy();
+ m_v_tangents.Destroy();
+
+ for (int i = 0; i < m_twists.Count(); i++)
+ m_twists[i].Destroy();
+ m_twists.Destroy();
+}
+
+bool ON_HermiteSurface::IsValid() const
+{
+ for (int i = 0; i < m_u_parameters.Count(); i++)
+ {
+ if (!ON_IsValid(m_u_parameters[i]))
+ return false;
+ }
+
+ for (int i = 0; i < m_v_parameters.Count(); i++)
+ {
+ if (!ON_IsValid(m_v_parameters[i]))
+ return false;
+ }
+
+ for (int i = 0; i < m_grid_points.Count(); i++)
+ {
+ for (int j = 0; j < m_grid_points[i].Count(); j++)
+ {
+ if (m_grid_points[i][j].IsUnset())
+ return false;
+ }
+ }
+
+ for (int i = 0; i < m_u_tangents.Count(); i++)
+ {
+ for (int j = 0; j < m_u_tangents[i].Count(); j++)
+ {
+ if (m_u_tangents[i][j].IsUnset())
+ return false;
+ }
+ }
+
+ for (int i = 0; i < m_v_tangents.Count(); i++)
+ {
+ for (int j = 0; j < m_v_tangents[i].Count(); j++)
+ {
+ if (m_v_tangents[i][j].IsUnset())
+ return false;
+ }
+ }
+
+ for (int i = 0; i < m_twists.Count(); i++)
+ {
+ for (int j = 0; j < m_twists[i].Count(); j++)
+ {
+ if (m_twists[i][j].IsUnset())
+ return false;
+ }
+ }
+
+ return ValidateHermiteData(
+ UParameters(),
+ VParameters(),
+ GridPoints(),
+ UTangents(),
+ VTangents(),
+ Twists()
+ );
+}
+
+bool ON_HermiteSurface::InBounds(int u, int v) const
+{
+ return (
+ 0 <= u &&
+ u < m_u_count &&
+ 0 <= v &&
+ v < m_v_count
+ );
+}
+
+double ON_HermiteSurface::UParameterAt(int u) const
+{
+ double rc = ON_UNSET_VALUE;
+ if (0 <= u && u < m_u_count)
+ rc = m_u_parameters[u];
+ return rc;
+}
+
+void ON_HermiteSurface::SetUParameterAt(int u, double param)
+{
+ if (0 <= u && u < m_u_count)
+ m_u_parameters[u] = param;
+}
+
+double ON_HermiteSurface::VParameterAt(int v) const
+{
+ double rc = ON_UNSET_VALUE;
+ if (0 <= v && v < m_v_count)
+ rc = m_v_parameters[v];
+ return rc;
+}
+
+void ON_HermiteSurface::SetVParameterAt(int v, double param)
+{
+ if (0 <= v && v < m_v_count)
+ m_v_parameters[v] = param;
+}
+
+ON_3dPoint ON_HermiteSurface::PointAt(int u, int v) const
+{
+ ON_3dPoint rc = ON_3dPoint::UnsetPoint;
+ if (InBounds(u, v))
+ rc = m_grid_points[v][u];
+ return rc;
+}
+
+void ON_HermiteSurface::SetPointAt(int u, int v, const ON_3dPoint& point)
+{
+ if (InBounds(u, v))
+ m_grid_points[v][u] = point;
+}
+
+ON_3dVector ON_HermiteSurface::UTangentAt(int u, int v) const
+{
+ ON_3dVector rc = ON_3dVector::UnsetVector;
+ if (InBounds(u, v))
+ rc = m_u_tangents[v][u];
+ return rc;
+}
+
+void ON_HermiteSurface::SetUTangentAt(int u, int v, const ON_3dVector& dir)
+{
+ if (InBounds(u, v))
+ m_u_tangents[v][u] = dir;
+}
+
+ON_3dVector ON_HermiteSurface::VTangentAt(int u, int v) const
+{
+ ON_3dVector rc = ON_3dVector::UnsetVector;
+ if (InBounds(u, v))
+ rc = m_v_tangents[v][u];
+ return rc;
+}
+
+void ON_HermiteSurface::SetVTangentAt(int u, int v, const ON_3dVector& dir)
+{
+ if (InBounds(u, v))
+ m_v_tangents[v][u] = dir;
+}
+
+ON_3dVector ON_HermiteSurface::TwistAt(int u, int v) const
+{
+ ON_3dVector rc = ON_3dVector::UnsetVector;
+ if (InBounds(u, v))
+ rc = m_twists[v][u];
+ return rc;
+}
+
+void ON_HermiteSurface::SetTwistAt(int u, int v, const ON_3dVector& dir)
+{
+ if (InBounds(u, v))
+ m_twists[v][u] = dir;
+}
+
+const ON_SimpleArray& ON_HermiteSurface::UParameters() const
+{
+ return m_u_parameters;
+}
+
+const ON_SimpleArray& ON_HermiteSurface::VParameters() const
+{
+ return m_v_parameters;
+}
+
+const ON_ClassArray>& ON_HermiteSurface::GridPoints() const
+{
+ return m_grid_points;
+}
+
+const ON_ClassArray>& ON_HermiteSurface::UTangents() const
+{
+ return m_u_tangents;
+}
+
+const ON_ClassArray>& ON_HermiteSurface::VTangents() const
+{
+ return m_v_tangents;
+}
+
+const ON_ClassArray>& ON_HermiteSurface::Twists() const
+{
+ return m_twists;
+}
+
+ON_NurbsSurface* ON_HermiteSurface::NurbsSurface(ON_NurbsSurface* pNurbsSurface)
+{
+ if (!IsValid())
+ return nullptr;
+
+ return ON_NurbsSurface::CreateHermiteSurface(
+ UParameters(),
+ VParameters(),
+ GridPoints(),
+ UTangents(),
+ VTangents(),
+ Twists(),
+ pNurbsSurface
+ );
+}
diff --git a/opennurbs_nurbssurface.h b/opennurbs_nurbssurface.h
index a13f734b..194c0130 100644
--- a/opennurbs_nurbssurface.h
+++ b/opennurbs_nurbssurface.h
@@ -1046,6 +1046,54 @@ public:
ON_BezierSurface& bezier_surface
) const;
+ /*
+ Description:
+ Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points.
+ Parameters:
+ u_Parameters
+ v_Parameters - [in] Specifies the "u" parameters defining the grid of parameter values
+ u_Parameters.Count()>1
+ u_Parameters are strictly increasing, i.e. u_Parameters[i] < u_Parameters[i+1]
+ same conditions on v_Parameters
+ Let n = u_Parameters.Count() and m = v_Parameters.Count().
+
+ Each of GridPoints, u_Tangents, v_Tangents and TwistVectors are data on a grid of points.
+ The size of each of these arrays must be n x m, so
+ GridPoints.Count() == n and GridPoints[i].Count() == m.
+
+ GridPoints - [in] Grid of points to interpolate.
+ u_Tangents - [in] Grid of Tangent directions to interpolate.
+ v_Tangents - [in] Grid of Tangent directions to interpolate.
+ TwistVectors - [in] Grid of twist vectors to interpolate.
+
+ hermite_surface -[in] optional existing ON_NurbsSurface returned here.
+ Returns:
+ A hermite-surface satisfying interpolation conditions. Null if error.
+ Notes:
+ The Hermite surface, H, is bicubic on each patch [u_i, u_(i+1)] x [v_j, v_(j+1)]
+ and satisfies
+ H( u_i, v_j) = GridData[i][j]
+ The first derivatives may be discontinuous at the knots
+ H_u_+/- ( u_i, v_j) = (Del_+/- u)_i * u_Tangents[i][j]
+ H_v_+/-( u_i, v_j) = (Del_+/- v)_i * v_Tangents[i][j]
+ Here the forward and backward difference operators
+ (Del_+ u)_i = u_(i+1) - u_i and (Del_- u)_i = u_i - u_(i-1)
+ are used for the forward (H_u_+) and backward (H_u_-) derivatives respectively.
+ Similarly the mixed partial derivative is defined by
+ H_uv_++ ( u_i, v_j) = (Del_+ u)_i * (Del_+ v)_j * TwistVector[i][j]
+ with 3 other possible variation in signs
+ */
+ static
+ class ON_NurbsSurface* CreateHermiteSurface(
+ const ON_SimpleArray& u_Parameters,
+ const ON_SimpleArray& v_Parameters,
+ const ON_ClassArray>& GridPoints,
+ const ON_ClassArray>& u_Tangents,
+ const ON_ClassArray>& v_Tangents,
+ const ON_ClassArray>& TwistVectors,
+ class ON_NurbsSurface* hermite_surface = 0);
+
+
/////////////////////////////////////////////////////////////////
// Implementation
public:
@@ -1993,6 +2041,92 @@ ON_NurbsSurface* ON_NurbsSurfaceQuadrilateral(
ON_NurbsSurface* nurbs_surface = nullptr
);
+
+#if defined(ON_DLL_TEMPLATE)
+ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray>;
+ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray>;
+#endif
+
+
+/*
+Description:
+ Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points.
+Remarks:
+ See static ON_NurbsSurface::CreateHermiteSurface for details.
+*/
+class ON_CLASS ON_HermiteSurface
+{
+public:
+ ON_HermiteSurface();
+ // Constructs a u_count by v_count grid.
+ ON_HermiteSurface(int u_count, int v_count);
+ ~ON_HermiteSurface();
+
+ // Constructs a u_count by v_count grid.
+ bool Create(int u_count, int v_count);
+ bool IsValid() const;
+
+ // Specifies the "u" parameters defining the grid of parameter values.
+ // These parameters are strictly increasing.
+ double UParameterAt(int u) const;
+ void SetUParameterAt(int u, double param);
+
+ // Specifies the "v" parameters defining the grid of parameter values.
+ // These parameters are strictly increasing.
+ double VParameterAt(int v) const;
+ void SetVParameterAt(int v, double param);
+
+ // Grid of points to interpolate.
+ ON_3dPoint PointAt(int u, int v) const;
+ void SetPointAt(int u, int v, const ON_3dPoint& point);
+
+ // Grid of "u" tangent directions to interpolate.
+ ON_3dVector UTangentAt(int u, int v) const;
+ void SetUTangentAt(int u, int v, const ON_3dVector& dir);
+
+ // Grid of "v" tangent directions to interpolate.
+ ON_3dVector VTangentAt(int u, int v) const;
+ void SetVTangentAt(int u, int v, const ON_3dVector& dir);
+
+ // Grid of twist vectors to interpolate.
+ ON_3dVector TwistAt(int u, int v) const;
+ void SetTwistAt(int u, int v, const ON_3dVector& dir);
+
+ // Create an ON_NurbsSurface satisfying interpolation conditions at a grid of points
+ ON_NurbsSurface* NurbsSurface(ON_NurbsSurface* pNurbsSurface = nullptr);
+
+public:
+ // The "u" parameters defining the grid of parameter values.
+ const ON_SimpleArray& UParameters() const;
+ // The "v" parameters defining the grid of parameter values.
+ const ON_SimpleArray& VParameters() const;
+ // Grid of points to interpolate.
+ const ON_ClassArray>& GridPoints() const;
+ // Grid of tangents in "u" direction to interpolate.
+ const ON_ClassArray>& UTangents() const;
+ // Grid of tangents in "v" direction to interpolate.
+ const ON_ClassArray>& VTangents() const;
+ // Grid of twist vectors to interpolate.
+ const ON_ClassArray>& Twists() const;
+
+private:
+ int m_u_count;
+ int m_v_count;
+ ON_SimpleArray m_u_parameters;
+ ON_SimpleArray m_v_parameters;
+ ON_ClassArray> m_grid_points;
+ ON_ClassArray> m_u_tangents;
+ ON_ClassArray> m_v_tangents;
+ ON_ClassArray> m_twists;
+
+private:
+ ON_HermiteSurface(const ON_HermiteSurface&) = delete;
+ ON_HermiteSurface& operator=(const ON_HermiteSurface&) = default;
+ bool InBounds(int u, int v) const;
+ void Destroy();
+};
+
+
#if defined(ON_DLL_TEMPLATE)
ON_DLL_TEMPLATE template class ON_CLASS ON_ClassArray;
ON_DLL_TEMPLATE template class ON_CLASS ON_ObjectArray;
diff --git a/opennurbs_object.cpp b/opennurbs_object.cpp
index 2cfb7247..ce331961 100644
--- a/opennurbs_object.cpp
+++ b/opennurbs_object.cpp
@@ -1389,7 +1389,7 @@ bool ON_Object::DetachUserData( ON_UserData* p )
ON_UserData* ON_Object::GetUserData( const ON_UUID& userdata_uuid ) const
-{
+ {
ON_UserData* prev = nullptr;
ON_UserData* p;
for ( p = m_userdata_list; p; prev = p, p = p->m_userdata_next )
diff --git a/opennurbs_plane.h b/opennurbs_plane.h
index dae820cf..6e9b213e 100644
--- a/opennurbs_plane.h
+++ b/opennurbs_plane.h
@@ -512,9 +512,14 @@ public:
*/
bool Flip();
-// world plane coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis);
- const static
- ON_Plane World_xy;
+ // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis);
+ const static ON_Plane World_xy;
+
+ // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis);
+ const static ON_Plane World_yz;
+
+ // world coordinate system ON_Plane(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis);
+ const static ON_Plane World_zx;
// All values are ON_UNSET_VALUE.
const static
diff --git a/opennurbs_point.h b/opennurbs_point.h
index 2c8b7abb..231757d1 100644
--- a/opennurbs_point.h
+++ b/opennurbs_point.h
@@ -1924,6 +1924,8 @@ public:
double y;
double z;
double d; // 4th coefficient of the plane equation.
+
+ void Dump(class ON_TextLog&) const;
};
#if defined(ON_DLL_TEMPLATE)
diff --git a/opennurbs_public.vcxproj b/opennurbs_public.vcxproj
index 98fa894b..8e73f6c6 100644
--- a/opennurbs_public.vcxproj
+++ b/opennurbs_public.vcxproj
@@ -22,31 +22,31 @@
{1356641D-0B22-4123-B519-A69EE5CDC7F8}
Win32Proj
opennurbs_public
- 8.1
+ 10.0
DynamicLibrary
true
- v141
+ v142
Unicode
DynamicLibrary
false
- v141
+ v142
Unicode
DynamicLibrary
true
- v141
+ v142
Unicode
DynamicLibrary
false
- v141
+ v142
Unicode
@@ -258,6 +258,7 @@
+
@@ -442,6 +443,7 @@
+
diff --git a/opennurbs_public.xcodeproj/project.pbxproj b/opennurbs_public.xcodeproj/project.pbxproj
index 3e3bf542..53fd52c4 100644
--- a/opennurbs_public.xcodeproj/project.pbxproj
+++ b/opennurbs_public.xcodeproj/project.pbxproj
@@ -11,6 +11,8 @@
1D4452FF1ED6454500CD7FC1 /* opennurbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D4452FE1ED6454500CD7FC1 /* opennurbs.h */; };
1D455699216D9DA100BC992F /* opennurbs_apple_nsfont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */; };
1D45569B216D9DAE00BC992F /* opennurbs_apple_nsfont.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */; };
+ 1D54D9D02388A90B0053ECCD /* opennurbs_symmetry.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */; };
+ 1D54D9D22388A9310053ECCD /* opennurbs_symmetry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */; };
1D741C1B21B9E3C700AA10E5 /* opennurbs_sleeplock.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */; };
1D741C1D21B9E3D700AA10E5 /* opennurbs_sleeplock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */; };
1D7B99581FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */; };
@@ -345,6 +347,8 @@
1D4452FE1ED6454500CD7FC1 /* opennurbs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs.h; sourceTree = ""; };
1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_apple_nsfont.cpp; sourceTree = ""; };
1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_apple_nsfont.h; sourceTree = ""; };
+ 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_symmetry.h; sourceTree = ""; };
+ 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_symmetry.cpp; sourceTree = ""; };
1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opennurbs_sleeplock.h; sourceTree = ""; };
1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_sleeplock.cpp; sourceTree = ""; };
1D7B99571FF3009600E9A791 /* opennurbs_unicode_cpsb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opennurbs_unicode_cpsb.cpp; sourceTree = ""; };
@@ -710,6 +714,7 @@
1DC319EF1ED653EE00DE6D26 /* Header Files */ = {
isa = PBXGroup;
children = (
+ 1D54D9CF2388A90B0053ECCD /* opennurbs_symmetry.h */,
1D741C1A21B9E3C600AA10E5 /* opennurbs_sleeplock.h */,
1D97149E218BC3B5008B4D65 /* opennurbs_testclass.h */,
1D45569A216D9DAE00BC992F /* opennurbs_apple_nsfont.h */,
@@ -870,6 +875,7 @@
1DC319F01ED6546C00DE6D26 /* Source Files */ = {
isa = PBXGroup;
children = (
+ 1D54D9D12388A9310053ECCD /* opennurbs_symmetry.cpp */,
1D741C1C21B9E3D600AA10E5 /* opennurbs_sleeplock.cpp */,
1D97149C218BC3A1008B4D65 /* opennurbs_testclass.cpp */,
1D455698216D9DA100BC992F /* opennurbs_apple_nsfont.cpp */,
@@ -1069,6 +1075,7 @@
1DC317E51ED652B800DE6D26 /* opennurbs_bitmap.h in Headers */,
1DC3182D1ED652B800DE6D26 /* opennurbs_function_list.h in Headers */,
1DC318081ED652B800DE6D26 /* opennurbs_cylinder.h in Headers */,
+ 1D54D9D02388A90B0053ECCD /* opennurbs_symmetry.h in Headers */,
1DC319981ED6534E00DE6D26 /* opennurbs_std_string.h in Headers */,
1DC318C21ED652F800DE6D26 /* opennurbs_leader.h in Headers */,
1DC3180C1ED652B800DE6D26 /* opennurbs_defines.h in Headers */,
@@ -1326,6 +1333,7 @@
1DC318C11ED652F800DE6D26 /* opennurbs_leader.cpp in Sources */,
1DC318C51ED652F800DE6D26 /* opennurbs_line.cpp in Sources */,
1DC318E21ED652F800DE6D26 /* opennurbs_model_component.cpp in Sources */,
+ 1D54D9D22388A9310053ECCD /* opennurbs_symmetry.cpp in Sources */,
1DC319AF1ED6534E00DE6D26 /* opennurbs_subd.cpp in Sources */,
1DC317DD1ED652B800DE6D26 /* opennurbs_base64.cpp in Sources */,
1DBFBF3C1EDF333C005B50AF /* opennurbs_memory_util.cpp in Sources */,
diff --git a/opennurbs_public_staticlib.vcxproj b/opennurbs_public_staticlib.vcxproj
index 042b70ab..bfdb5da6 100644
--- a/opennurbs_public_staticlib.vcxproj
+++ b/opennurbs_public_staticlib.vcxproj
@@ -22,32 +22,32 @@
{23288C65-E3EB-4D09-A648-22E1636EB40F}
Win32Proj
opennurbs_public_staticlib
- 8.1
+ 10.0
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
true
Unicode
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
true
Unicode
@@ -258,6 +258,7 @@
+
@@ -441,6 +442,7 @@
+
diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h
index 55d195f7..cf9bd6a2 100644
--- a/opennurbs_public_version.h
+++ b/opennurbs_public_version.h
@@ -13,11 +13,11 @@
// These are set automatically by the build system as the
// first step in each build.
//
-#define RMA_VERSION_YEAR 2019
-#define RMA_VERSION_MONTH 11
-#define RMA_VERSION_DATE 5
-#define RMA_VERSION_HOUR 17
-#define RMA_VERSION_MINUTE 46
+#define RMA_VERSION_YEAR 2020
+#define RMA_VERSION_MONTH 1
+#define RMA_VERSION_DATE 16
+#define RMA_VERSION_HOUR 10
+#define RMA_VERSION_MINUTE 47
////////////////////////////////////////////////////////////////
//
@@ -35,9 +35,9 @@
// 3 = build system release build
#define RMA_VERSION_BRANCH 0
-#define VERSION_WITH_COMMAS 7,0,19309,17460
-#define VERSION_WITH_PERIODS 7.0.19309.17460
-#define COPYRIGHT "Copyright (C) 1993-2019, Robert McNeel & Associates. All Rights Reserved."
+#define VERSION_WITH_COMMAS 7,0,20016,10470
+#define VERSION_WITH_PERIODS 7.0.20016.10470
+#define COPYRIGHT "Copyright (C) 1993-2020, Robert McNeel & Associates. All Rights Reserved."
#define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library."
#define RMA_VERSION_NUMBER_MAJOR_STRING "7"
@@ -47,8 +47,8 @@
#define RMA_VERSION_NUMBER_SR_STRING "SR0"
#define RMA_VERSION_NUMBER_SR_WSTRING L"SR0"
-#define RMA_VERSION_WITH_PERIODS_STRING "7.0.19309.17460"
-#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.19309.17460"
+#define RMA_VERSION_WITH_PERIODS_STRING "7.0.20016.10470"
+#define RMA_VERSION_WITH_PERIODS_WSTRING L"7.0.20016.10470"
diff --git a/opennurbs_statics.cpp b/opennurbs_statics.cpp
index 9b379a06..45d251b8 100644
--- a/opennurbs_statics.cpp
+++ b/opennurbs_statics.cpp
@@ -651,6 +651,22 @@ const ON_3fVector ON_3fVector::ZAxis(0.0f, 0.0f, 1.0f);
const ON_WindingNumber ON_WindingNumber::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_WindingNumber);
+const double ON_Symmetry::ZeroTolerance = 1.0e-8;
+
+const ON_Symmetry ON_Symmetry::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_Symmetry);
+
+// {3C6C7ABD-F3D5-41C2-96C9-DB9AEAF06E90}
+const ON_UUID ON_Symmetry::ReflectId =
+{ 0x3c6c7abd, 0xf3d5, 0x41c2, { 0x96, 0xc9, 0xdb, 0x9a, 0xea, 0xf0, 0x6e, 0x90 } };
+
+// {C1592254-DEAC-4E8E-B01E-0522450E03F7}
+const ON_UUID ON_Symmetry::RotateId =
+{ 0xc1592254, 0xdeac, 0x4e8e, { 0xb0, 0x1e, 0x5, 0x22, 0x45, 0xe, 0x3, 0xf7 } };
+
+// {9133927D-5A4E-4DDD-9924-EF3A6360C19A}
+const ON_UUID ON_Symmetry::ReflectAndRotateId =
+{ 0x9133927d, 0x5a4e, 0x4ddd, { 0x99, 0x24, 0xef, 0x3a, 0x63, 0x60, 0xc1, 0x9a } };
+
static ON_BoundingBox BoundingBoxInit(double x)
{
ON_BoundingBox bbox;
@@ -848,10 +864,14 @@ const ON_PlaneEquation ON_PlaneEquation::UnsetPlaneEquation(ON_UNSET_VALUE, ON_U
const ON_PlaneEquation ON_PlaneEquation::ZeroPlaneEquation(0.0, 0.0, 0.0, 0.0);
const ON_PlaneEquation ON_PlaneEquation::NanPlaneEquation(ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN, ON_DBL_QNAN);
-const ON_Plane ON_xy_plane(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis);
-const ON_Plane ON_yz_plane(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis);
-const ON_Plane ON_zx_plane(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis);
-const ON_Plane ON_Plane::World_xy = ON_xy_plane;
+const ON_Plane ON_Plane::World_xy(ON_3dPoint::Origin, ON_3dVector::XAxis, ON_3dVector::YAxis);
+const ON_Plane ON_Plane::World_yz(ON_3dPoint::Origin, ON_3dVector::YAxis, ON_3dVector::ZAxis);
+const ON_Plane ON_Plane::World_zx(ON_3dPoint::Origin, ON_3dVector::ZAxis, ON_3dVector::XAxis);
+
+// obsolete names for world planes
+const ON_Plane ON_xy_plane = ON_Plane::World_xy;
+const ON_Plane ON_yz_plane = ON_Plane::World_yz;
+const ON_Plane ON_zx_plane = ON_Plane::World_zx;
static ON_Plane ON_Plane_UnsetPlane()
{
@@ -2330,6 +2350,8 @@ const ON_SubDEdgePtr ON_SubDEdgePtr::Null = { 0 };
const ON_SubDFacePtr ON_SubDFacePtr::Null = { 0 };
const ON_SubDComponentPtr ON_SubDComponentPtr::Null = { 0 };
const ON_SubDComponentPtrPair ON_SubDComponentPtrPair::Null = ON_SubDComponentPtrPair::Create(ON_SubDComponentPtr::Null,ON_SubDComponentPtr::Null);
+const ON_SubDComponentList ON_SubDComponentList::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentList);
+
const ON_SubDEdgeChain ON_SubDEdgeChain::Empty ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDEdgeChain);
@@ -2373,9 +2395,9 @@ const double ON_SubDSectorType::SmoothSectorTheta = 0.5*ON_PI;
const double ON_SubDSectorType::UnsetSectorTheta = -8882.0;
const double ON_SubDSectorType::ErrorSectorTheta = -9992.0;
-const double ON_SubDSectorType::IgnoredSectorWeight = 0.0;
-const double ON_SubDSectorType::UnsetSectorWeight = -8883.0;
-const double ON_SubDSectorType::ErrorSectorWeight = -9993.0;
+const double ON_SubDSectorType::IgnoredSectorCoefficient = 0.0;
+const double ON_SubDSectorType::UnsetSectorCoefficient = -8883.0;
+const double ON_SubDSectorType::ErrorSectorCoefficient = -9993.0;
const ON_SubDComponentRegionIndex ON_SubDComponentRegionIndex::Zero ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_SubDComponentRegionIndex);
@@ -2526,6 +2548,11 @@ const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshCrease = ON_S
const ON_ToSubDParameters ON_ToSubDParameters::InteriorCreaseAtMeshEdge = ON_SubDCreaseParameters_CreaseAt(ON_ToSubDParameters::InteriorCreaseOption::AtMeshEdge);
const ON_ToSubDParameters ON_ToSubDParameters::ConvexCornerAtMeshCorner = ON_SubDCreaseParameters_ConvexCorners();
+const ON_SubDComponentFilter ON_SubDComponentFilter::Unset = ON_SubDComponentFilter::Create(true, true, true);
+const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyVertices = ON_SubDComponentFilter::Create(true, false, false);
+const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyEdges = ON_SubDComponentFilter::Create(false, true, false);
+const ON_SubDComponentFilter ON_SubDComponentFilter::OnlyFaces = ON_SubDComponentFilter::Create(false, false, true);
+
unsigned int ON_ModelComponent::Internal_SystemComponentHelper()
{
static unsigned int rc = 0;
diff --git a/opennurbs_subd.cpp b/opennurbs_subd.cpp
index 49f73126..6cd4b238 100644
--- a/opennurbs_subd.cpp
+++ b/opennurbs_subd.cpp
@@ -10,7 +10,6 @@
#include "opennurbs_subd_data.h"
-/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved.
@@ -389,6 +388,42 @@ const class ON_SubDVertex* ON_SubDEdgePtr::RelativeVertex(
return nullptr;
}
+const ON_3dPoint ON_SubDEdgePtr::RelativeControlNetPoint(
+ int relative_vertex_index
+) const
+{
+ const ON_SubDVertex* v = RelativeVertex(relative_vertex_index);
+ return (nullptr != v) ? v->ControlNetPoint() : ON_3dPoint::NanPoint;
+}
+
+const ON_Line ON_SubDEdgePtr::RelativeControlNetLine() const
+{
+ return ON_Line(RelativeControlNetPoint(0), RelativeControlNetPoint(1));
+}
+
+const ON_3dVector ON_SubDEdgePtr::RelativeControlNetDirection() const
+{
+ return RelativeControlNetLine().Direction();
+}
+
+double ON_SubDEdgePtr::RelativeSectorCoefficient(
+ int relative_vertex_index
+) const
+{
+ for (;;)
+ {
+ if (relative_vertex_index < 0 || relative_vertex_index>1)
+ break;
+ const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_ptr);
+ if (nullptr == edge)
+ break;
+ if (0 != ON_SUBD_EDGE_DIRECTION(m_ptr))
+ relative_vertex_index = 1 - relative_vertex_index;
+ return edge->m_sector_coefficient[relative_vertex_index];
+ }
+ return ON_SubDSectorType::ErrorSectorCoefficient;
+}
+
const ON_3dVector ON_SubDEdgePtr::RelativeDirection() const
{
for (;;)
@@ -441,6 +476,38 @@ const ON_SubDEdgePtr ON_SubDEdgePtr::Create(
return ON_SubDEdgePtr::Create(edge_element.Edge(), edge_element.ComponentDirection());
}
+const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromStartVertex(
+ const class ON_SubDEdge* edge,
+ const ON_SubDVertex* start_vertex
+)
+{
+ for (;;)
+ {
+ if (nullptr == edge || nullptr == start_vertex)
+ break;
+ if (edge->m_vertex[0] == edge->m_vertex[1])
+ break;
+
+ ON__UINT_PTR dir;
+ if (start_vertex == edge->m_vertex[0])
+ dir = 0;
+ else if (start_vertex == edge->m_vertex[1])
+ dir = 1;
+ else
+ break;
+ return ON_SubDEdgePtr::Create(edge, dir);
+ }
+ return ON_SubDEdgePtr::Null;
+}
+
+const ON_SubDEdgePtr ON_SubDEdgePtr::CreateFromEndVertex(
+ const class ON_SubDEdge* edge,
+ const ON_SubDVertex* end_vertex
+)
+{
+ return CreateFromStartVertex(edge,end_vertex).Reversed();
+}
+
//////////////////////////////////////////////////////////////////////////
//
// ON_SubDFacePtr
@@ -484,6 +551,11 @@ const ON_ComponentStatus ON_SubDFacePtr::Status() const
return (nullptr == face) ? ON_ComponentStatus::NoneSet : face->m_status;
}
+const ON_SubDFacePtr ON_SubDFacePtr::Reversed() const
+{
+ return ON_SubDFacePtr::Create(ON_SUBD_FACE_POINTER(m_ptr), 1 - (m_ptr & 1));
+}
+
const ON_SubDFacePtr ON_SubDFacePtr::Create(
const class ON_SubDFace* face,
ON__UINT_PTR direction
@@ -1333,6 +1405,21 @@ const ON_SubDComponentPtr ON_SubDComponentPtrPair::Second() const
return m_pair[1];
}
+bool ON_SubDComponentPtrPair::FirstIsNull() const
+{
+ return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr));
+}
+
+bool ON_SubDComponentPtrPair::SecondIsNull() const
+{
+ return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr));
+}
+
+bool ON_SubDComponentPtrPair::BothAreNull() const
+{
+ return (0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[0].m_ptr)) && 0 == (ON_SUBD_COMPONENT_POINTER_MASK & m_pair[1].m_ptr);
+}
+
//////////////////////////////////////////////////////////////////////////
//
// ON_ToSubDParameters
@@ -1625,6 +1712,54 @@ unsigned int ON_SubDVertex::MarkedFaceCount() const
return mark_count;
}
+unsigned int ON_SubDVertex::MinimumFaceEdgeCount() const
+{
+ unsigned short min_count = 0xFFFFU;
+ for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
+ {
+ const ON_SubDFace* f = m_faces[vfi];
+ if (nullptr != f && f->m_edge_count < min_count)
+ min_count = f->m_edge_count;
+ }
+ return min_count < 0xFFFFU ? min_count : 0;
+}
+
+unsigned int ON_SubDVertex::MaximumFaceEdgeCount() const
+{
+ unsigned short max_count = 0;
+ for (unsigned short vfi = 0; vfi < m_face_count; ++vfi)
+ {
+ const ON_SubDFace* f = m_faces[vfi];
+ if (nullptr != f && f->m_edge_count < max_count)
+ max_count = f->m_edge_count;
+ }
+ return max_count;
+}
+
+
+unsigned int ON_SubDVertex::MinimumEdgeFaceCount() const
+{
+ unsigned short min_count = 0xFFFFU;
+ for (unsigned short vei = 0; vei < m_edge_count; ++vei)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
+ if (nullptr != e && e->m_face_count < min_count)
+ min_count = e->m_face_count;
+ }
+ return min_count < 0xFFFFU ? min_count : 0;
+}
+
+unsigned int ON_SubDVertex::MaximumEdgeFaceCount() const
+{
+ unsigned short max_count = 0;
+ for (unsigned short vei = 0; vei < m_edge_count; ++vei)
+ {
+ const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
+ if (nullptr != e && e->m_face_count > max_count)
+ max_count = e->m_face_count;
+ }
+ return max_count;
+}
unsigned int ON_SubDEdge::MarkedVertexCount() const
{
@@ -1761,13 +1896,69 @@ bool ON_SubDVertex::IsSmoothOrCrease() const
return (ON_SubD::VertexTag::Smooth == m_vertex_tag || ON_SubD::VertexTag::Crease == m_vertex_tag);
}
+bool ON_SubDVertex::GetBoundaryVertexEdges(
+ ON_SubDEdgePtr* eptr0,
+ ON_SubDEdgePtr* eptr1
+) const
+{
+ unsigned int vbi[2] = {};
+ const bool rc = GetBoundaryVertexEdgeIndices(&vbi[0], &vbi[1]);
+ if (rc)
+ {
+ if (nullptr != eptr0)
+ *eptr0 = m_edges[vbi[0]];
+ if (nullptr != eptr1)
+ *eptr1 = m_edges[vbi[1]];
+ }
+ else
+ {
+ if (nullptr != eptr0)
+ *eptr0 = ON_SubDEdgePtr::Null;
+ if (nullptr != eptr1)
+ *eptr1 = ON_SubDEdgePtr::Null;
+ }
+ return rc;
+}
+
+bool ON_SubDVertex::GetBoundaryVertexEdgeIndices(
+ unsigned* vei0,
+ unsigned* vei1
+) const
+{
+ unsigned int vbi_count = 0;
+ unsigned int vbi[2] = {};
+ for (unsigned short vei = 0; vei < m_edge_count; vei++)
+ {
+ const ON_SubDEdge* e = m_edges[vei].Edge();
+ if (1 == e->m_face_count)
+ {
+ if (vbi_count < 2)
+ vbi[vbi_count++] = vei;
+ else
+ {
+ vbi_count = 0;
+ break;
+ }
+ }
+ }
+ if (2 != vbi_count)
+ vbi[0] = vbi[1] = ON_UNSET_UINT_INDEX;
+ if (nullptr != vei0)
+ *vei0 = vbi[0];
+ if (nullptr != vei1)
+ *vei1 = vbi[1];
+ return (2 == vbi_count);
+}
+
const ON_SubDVertexEdgeProperties ON_SubDVertex::EdgeProperties() const
{
ON_SubDVertexEdgeProperties ep;
+ ep.m_edge_count = m_edge_count;
+ ep.m_face_count = m_face_count;
+
bool bFirstEdge = true;
- const unsigned short edge_count = m_edge_count;
- for (unsigned short vei = 0; vei < edge_count; vei++)
+ for (unsigned short vei = 0; vei < ep.m_edge_count; vei++)
{
const ON_SubDEdge* e = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
if (nullptr == e)
@@ -1974,6 +2165,41 @@ bool ON_SubDVertex::HasBoundaryVertexTopology() const
return false;
}
+
+bool ON_SubDVertexEdgeProperties::HasInteriorVertexTopology() const
+{
+ return
+ m_null_edge_count == 0
+ && m_edge_count == m_face_count
+ && m_boundary_edge_count == 0
+ && m_interior_edge_count >= 2
+ && m_nonmanifold_edge_count == 0
+ ;
+}
+
+bool ON_SubDVertexEdgeProperties::HasBoundaryVertexTopology() const
+{
+ return
+ m_null_edge_count == 0
+ && m_edge_count == m_face_count+1
+ && m_boundary_edge_count == 2
+ && m_nonmanifold_edge_count == 0
+ ;
+}
+
+bool ON_SubDVertexEdgeProperties::HasManifoldVertexTopology() const
+{
+ return HasInteriorVertexTopology() || HasBoundaryVertexTopology();
+}
+
+bool ON_SubDVertexEdgeProperties::HasNonmanifoldVertexTopology() const
+{
+ return
+ (m_null_edge_count == 0)
+ && (m_wire_edge_count > 0 || m_nonmanifold_edge_count > 0)
+ ;
+}
+
bool ON_SubDVertex::IsStandard() const
{
if (nullptr == m_edges)
@@ -2481,7 +2707,7 @@ unsigned int ON_SubDEdge::FaceArrayIndex(
{
if (f == ON_SUBD_FACE_POINTER(m_face2[0].m_ptr))
return 0;
- if (face_count >= 1)
+ if (face_count > 1)
{
if (f == ON_SUBD_FACE_POINTER(m_face2[1].m_ptr))
return 1;
@@ -2691,6 +2917,11 @@ const ON_3dPoint ON_SubDEdge::ControlNetPoint( unsigned int i) const
return (ON_3dPoint(m_vertex[i]->m_P));
}
+const ON_Line ON_SubDEdge::ControlNetLine() const
+{
+ return ON_Line(ControlNetPoint(0), ControlNetPoint(1));
+}
+
const ON_3dVector ON_SubDEdge::ControlNetDirection() const
{
if (nullptr == m_vertex[0] || nullptr == m_vertex[1])
@@ -2786,6 +3017,13 @@ void ON_SubDFace::CopyFrom(
else
m_edge_count = 0;
}
+
+ // RH-56133 need to copy texture coordinate information that was recently added.
+ m_texture_coordinate_origin[0] = src->m_texture_coordinate_origin[0];
+ m_texture_coordinate_origin[1] = src->m_texture_coordinate_origin[1];
+ m_texture_coordinate_delta[0] = src->m_texture_coordinate_delta[0];
+ m_texture_coordinate_delta[1] = src->m_texture_coordinate_delta[1];
+ m_texture_coordinate_bits = src->m_texture_coordinate_bits;
}
const ON_SubDEdgePtr ON_SubDFace::EdgePtr(
@@ -3575,11 +3813,11 @@ static bool ON_SubDIsNotValid(bool bSilentError)
return bSilentError ? false : ON_IsNotValid();
}
-static bool EdgeVertexWeightIsSet(
- double edge_vertex_weight
+static bool EdgeSectorCoefficientIsSet(
+ double edge_sector_coefficient
)
{
- return (0.0 < edge_vertex_weight && edge_vertex_weight < 1.0);
+ return (0.0 < edge_sector_coefficient && edge_sector_coefficient < 1.0);
}
static bool EdgeSectorWeightIsValid(
@@ -3590,7 +3828,7 @@ static bool EdgeSectorWeightIsValid(
if (0.0 <= edge_vertex_weight && edge_vertex_weight < 1.0)
return true;
- if (ON_SubDSectorType::UnsetSectorWeight == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel())
+ if (ON_SubDSectorType::UnsetSectorCoefficient == edge_vertex_weight && nullptr != edge && 0 == edge->SubdivisionLevel())
return true;
return false;
@@ -3644,11 +3882,11 @@ static bool IsValidVertexEdgeLink(
if (!st.IsValid())
return ON_SubDIsNotValid(bSilentError);
- const double expected_vertex_weight = st.SectorWeight();
- if (false == (expected_vertex_weight == edge->m_sector_coefficient[end_index]))
+ const double expected_sector_coefficient = st.SectorCoefficient();
+ if (false == (expected_sector_coefficient == edge->m_sector_coefficient[end_index]))
return ON_SubDIsNotValid(bSilentError);
- if (false == EdgeVertexWeightIsSet(expected_vertex_weight))
+ if (false == EdgeSectorCoefficientIsSet(expected_sector_coefficient))
return ON_SubDIsNotValid(bSilentError);
}
}
@@ -3891,10 +4129,18 @@ static bool IsValidSubDVertexTag(
if (false == bValidEdgeTags)
break; // invalid edge tags detected in IsValidSubDEdgeTag();
- if ( crease_edge_count < 2 )
+ if (0 == crease_edge_count)
{
+ // currently, isolated vertices are not permitted - may change in the future
return ON_SubDIsNotValid(bSilentError);
}
+ if (1 == crease_edge_count)
+ {
+ // must be a single wire crease edge ending at this vertex
+ if ( 1 != vertex_edge_count || 0 != vertex->m_face_count)
+ return ON_SubDIsNotValid(bSilentError);
+
+ }
break;
case ON_SubD::VertexTag::Dart:
@@ -4341,9 +4587,11 @@ bool ON_SubDimple::IsValidLevel(
if ( e_id_range[1] > m_max_edge_id )
return ON_SubDIsNotValid(bSilentError);
- // currently, wire edges are not permitted
- if (wire_edge_count > 0)
- return ON_SubDIsNotValid(bSilentError);
+ // As of NOvember 12, 2019
+ // Wire edges are permitted. THey exist in subds being edited.
+ ////// currently, wire edges are not permitted
+ ////if (wire_edge_count > 0)
+ //// return ON_SubDIsNotValid(bSilentError);
// simple face validation
if (level_index == subd.ActiveLevelIndex())
@@ -4596,6 +4844,8 @@ unsigned int ON_SubD::DumpTopology(
text_log.Print(L"SubD[%" PRIu64 "]: texture domain type = %ls.\n", runtime_sn, static_cast(subd_texture_domain));
+ text_log.Print(L"Levels:\n");
+
ON_SubDLevelIterator lit(subdimple->LevelIterator());
const ON_2udex empty_id_range(ON_UNSET_UINT_INDEX, 0);
@@ -4671,7 +4921,67 @@ unsigned int ON_SubDLevel::DumpTopology(
unsigned int edge_error_count = 0;
unsigned int face_error_count = 0;
- text_log.Print(L"SubD level %u topology: %u vertices, %u edges, ", m_level_index, m_vertex_count, m_edge_count);
+ text_log.Print(L"SubD level %u topology: %u vertices, %u edges", m_level_index, m_vertex_count, m_edge_count);
+
+
+ unsigned int wire_edge_count = 0U;
+ unsigned int boundary_edge_count = 0U;
+ unsigned int interior_edge_count = 0U;
+ unsigned int nonmanifold_edge_count = 0U;
+ for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
+ {
+ if (0 == e->m_face_count)
+ ++wire_edge_count;
+ else if (1 == e->m_face_count)
+ ++boundary_edge_count;
+ else if (2 == e->m_face_count)
+ ++interior_edge_count;
+ else if (e->m_face_count >= 3)
+ ++nonmanifold_edge_count;
+ }
+
+ if (wire_edge_count > 0U)
+ {
+ if (nonmanifold_edge_count > 0U)
+ {
+ if (boundary_edge_count > 0U && interior_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u interior, %u wire, %u nonmanifold)", boundary_edge_count, interior_edge_count, wire_edge_count, nonmanifold_edge_count);
+ else if (boundary_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u wire, %u nonmanifold)", boundary_edge_count, wire_edge_count, nonmanifold_edge_count);
+ else if (interior_edge_count > 0U)
+ text_log.Print(L" (%u interior, %u wire, %u nonmanifold)", interior_edge_count, wire_edge_count, nonmanifold_edge_count);
+ else
+ text_log.Print(L" (%u wire, %u nonmanifold)", wire_edge_count, nonmanifold_edge_count);
+ }
+ else
+ {
+ if (boundary_edge_count > 0U && interior_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u interior, %u wire)", boundary_edge_count, interior_edge_count, wire_edge_count);
+ else if (boundary_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u wire)", boundary_edge_count, wire_edge_count);
+ else if (interior_edge_count > 0U)
+ text_log.Print(L" (%u interior, %u wire)", interior_edge_count, wire_edge_count);
+ else
+ text_log.Print(L" (%u wire)", wire_edge_count);
+ }
+ }
+ else if (nonmanifold_edge_count > 0U)
+ {
+ if (boundary_edge_count > 0U && interior_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u interior, %u nonmanifold)", boundary_edge_count, interior_edge_count, nonmanifold_edge_count);
+ else if (boundary_edge_count > 0U)
+ text_log.Print(L" (%u boundary, %u nonmanifold)", boundary_edge_count, nonmanifold_edge_count);
+ else if (interior_edge_count > 0U)
+ text_log.Print(L" (%u interior, %u nonmanifold)", interior_edge_count, nonmanifold_edge_count);
+ else
+ text_log.Print(L" (%u nonmanifold)", nonmanifold_edge_count);
+ }
+ else if (boundary_edge_count > 0U && interior_edge_count > 0U)
+ {
+ text_log.Print(L" (%u boundary, %u interior)", boundary_edge_count, interior_edge_count);
+ }
+
+ text_log.Print(L", ");
unsigned int ngon_count[65] = {};
unsigned int maxN = (unsigned int)(sizeof(ngon_count) / sizeof(ngon_count[0])) - 1;
@@ -4729,6 +5039,80 @@ unsigned int ON_SubDLevel::DumpTopology(
if (IsEmpty())
return 0;
+ unsigned int damaged_vertex_count = 0;
+ unsigned int damaged_edge_count = 0;
+ unsigned int damaged_face_count = 0;
+ enum : unsigned int
+ {
+ damaged_id_list_capacity = 8
+ };
+ for (;;)
+ {
+ unsigned int damaged_vertex_id[damaged_id_list_capacity] = {};
+ unsigned int damaged_edge_id[damaged_id_list_capacity] = {};
+ unsigned int damaged_face_id[damaged_id_list_capacity] = {};
+ for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
+ {
+ if (false == v->m_status.IsDamaged())
+ continue;
+ if (damaged_vertex_count < damaged_id_list_capacity)
+ damaged_vertex_id[damaged_vertex_count] = v->m_id;
+ ++damaged_vertex_count;
+ }
+ for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
+ {
+ if (false == e->m_status.IsDamaged())
+ continue;
+ if (damaged_edge_count < damaged_id_list_capacity)
+ damaged_edge_id[damaged_edge_count] = e->m_id;
+ ++damaged_edge_count;
+ }
+ for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
+ {
+ if (false == f->m_status.IsDamaged())
+ continue;
+ if (damaged_face_count < damaged_id_list_capacity)
+ damaged_face_id[damaged_face_count] = f->m_id;
+ ++damaged_face_count;
+ }
+
+ if (0U == damaged_vertex_count && 0U == damaged_edge_count && 0U == damaged_face_count)
+ break;
+ text_log.Print("DAMAGED SubD level:\n");
+ ON_TextLogIndent indent1(text_log);
+ if (damaged_vertex_count > 0)
+ {
+ text_log.Print(L"%u DAMAGED vertices: ", damaged_vertex_count);
+ text_log.Print("v%u", damaged_vertex_id[0]);
+ for (unsigned i = 1U; i < damaged_vertex_count; ++i)
+ text_log.Print(", v%u", damaged_vertex_id[i]);
+ if (damaged_vertex_count > damaged_id_list_capacity)
+ text_log.Print(", ...");
+ text_log.PrintNewLine();
+ }
+ if (damaged_edge_count > 0)
+ {
+ text_log.Print(L"%u DAMAGED edges: ", damaged_edge_count);
+ text_log.Print("e%u", damaged_edge_id[0]);
+ for (unsigned i = 1U; i < damaged_edge_count; ++i)
+ text_log.Print(", e%u", damaged_edge_id[i]);
+ if (damaged_edge_count > damaged_id_list_capacity)
+ text_log.Print(", ...");
+ text_log.PrintNewLine();
+ }
+ if (damaged_face_count > 0)
+ {
+ text_log.Print(L"%u DAMAGED faces: ", damaged_face_count);
+ text_log.Print("f%u", damaged_face_id[0]);
+ for (unsigned i = 1U; i < damaged_face_count; ++i)
+ text_log.Print(", f%u", damaged_face_id[i]);
+ if (damaged_face_count > damaged_id_list_capacity)
+ text_log.Print(", ...");
+ text_log.PrintNewLine();
+ }
+ break;
+ }
+
///////////////////////////////////////////////////////////////////
//
// Vertex Topology
@@ -4736,10 +5120,12 @@ unsigned int ON_SubDLevel::DumpTopology(
// vEdges[vertex_edge_count] = { +eA, -eB, ... }
// vFaces[vertex_edge_count] = { fP, fQ, fR, ... }
//
+ damaged_vertex_count = 0U;
unsigned int vertex_count = 0;
unsigned int vertex_dump_count = 0;
ON_2udex skipped_vertex_id = ON_2udex::Zero;
unsigned int max_vertex_id = 0;
+ bool bSkippedPreviousComponent = false;
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
{
if (vertex_count >= m_vertex_count && v->SubdivisionLevel() != level_index)
@@ -4748,8 +5134,11 @@ unsigned int ON_SubDLevel::DumpTopology(
max_vertex_id = v->m_id;
vertex_count++;
+ const bool bIsDamaged = v->m_status.IsDamaged();
+ if (bIsDamaged)
+ ++damaged_vertex_count;
- if (bVertexIdTest)
+ if (bVertexIdTest && (false == bIsDamaged || damaged_vertex_count > damaged_id_list_capacity))
{
bool bSkip = true;
for (;;)
@@ -4780,6 +5169,7 @@ unsigned int ON_SubDLevel::DumpTopology(
skipped_vertex_id.i = v->m_id;
else if (v->m_id > skipped_vertex_id.j)
skipped_vertex_id.j = v->m_id;
+ bSkippedPreviousComponent = true;
continue;
}
}
@@ -4814,12 +5204,29 @@ unsigned int ON_SubDLevel::DumpTopology(
break;
}
- text_log.Print(
- "v%u: %ls (%g, %g, %g)\n",
- v->m_id,
- static_cast(vtag),
- P0.x, P0.y, P0.z
- );
+ if (bSkippedPreviousComponent)
+ {
+ text_log.Print(L"...\n");
+ bSkippedPreviousComponent = false;
+ }
+ if (bIsDamaged)
+ {
+ text_log.Print(
+ "v%u: (DAMAGED) %ls (%g, %g, %g)\n",
+ v->m_id,
+ static_cast(vtag),
+ P0.x, P0.y, P0.z
+ );
+ }
+ else
+ {
+ text_log.Print(
+ "v%u: %ls (%g, %g, %g)\n",
+ v->m_id,
+ static_cast(vtag),
+ P0.x, P0.y, P0.z
+ );
+ }
text_log.PushIndent();
@@ -4916,10 +5323,14 @@ unsigned int ON_SubDLevel::DumpTopology(
// eN (+vA, -vB)
// eFaces[edge_face_count] = { fP, fQ, fR, ... }
//
+ damaged_edge_count = 0U;
+ wire_edge_count = 0U;
+ nonmanifold_edge_count = 0U;
unsigned int edge_count = 0;
unsigned int edge_dump_count = 0;
ON_2udex skipped_edge_id = ON_2udex::Zero;
unsigned int max_edge_id = 0;
+ bSkippedPreviousComponent = false;
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
{
if (edge_count >= m_edge_count && e->SubdivisionLevel() != level_index)
@@ -4928,7 +5339,22 @@ unsigned int ON_SubDLevel::DumpTopology(
max_edge_id = e->m_id;
edge_count++;
- if (bEdgeIdTest)
+ const bool bIsDamaged = e->m_status.IsDamaged();
+ if (bIsDamaged)
+ ++damaged_edge_count;
+ const bool bIsWireEdge = (0U == e->m_face_count);
+ if (bIsWireEdge)
+ ++wire_edge_count;
+ const bool bIsNonmanifoldEdge = (e->m_face_count >= 3U);
+ if (bIsNonmanifoldEdge)
+ ++nonmanifold_edge_count;
+
+ if (
+ bEdgeIdTest
+ && (false == bIsDamaged || damaged_edge_count > damaged_id_list_capacity)
+ && (false == bIsWireEdge || wire_edge_count > damaged_id_list_capacity)
+ && (false == bIsNonmanifoldEdge || nonmanifold_edge_count > damaged_id_list_capacity)
+ )
{
bool bSkip = true;
for (;;)
@@ -4959,6 +5385,7 @@ unsigned int ON_SubDLevel::DumpTopology(
skipped_edge_id.i = e->m_id;
else if (e->m_id > skipped_edge_id.j)
skipped_edge_id.j = e->m_id;
+ bSkippedPreviousComponent = true;
continue;
}
}
@@ -4988,11 +5415,65 @@ unsigned int ON_SubDLevel::DumpTopology(
break;
}
- text_log.Print(
- "e%u: %ls (",
- e->m_id,
- static_cast(etag)
- );
+ if (bSkippedPreviousComponent)
+ {
+ text_log.Print(L"...\n");
+ bSkippedPreviousComponent = false;
+ }
+ if (bIsDamaged)
+ {
+ if (bIsWireEdge)
+ {
+ text_log.Print(
+ "e%u: (DAMAGED) %ls wire (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ else if (bIsNonmanifoldEdge)
+ {
+ text_log.Print(
+ "e%u: (DAMAGED) %ls nonmanifold (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ else
+ {
+ text_log.Print(
+ "e%u: (DAMAGED) %ls (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ }
+ else
+ {
+ if (bIsWireEdge)
+ {
+ text_log.Print(
+ "e%u: wire %ls (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ else if (bIsNonmanifoldEdge)
+ {
+ text_log.Print(
+ "e%u: nonmanifold %ls (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ else
+ {
+ text_log.Print(
+ "e%u: %ls (",
+ e->m_id,
+ static_cast(etag)
+ );
+ }
+ }
prefix[0] = ON_String::Space;
prefix[1] = error_code_point;
@@ -5013,7 +5494,7 @@ unsigned int ON_SubDLevel::DumpTopology(
}
if (error_code_point == prefix[1])
edge_error_count++;
- text_log.Print("%s%u", prefix, vid);
+ text_log.Print("%s%u", (0==evi)?(prefix+1):(prefix), vid);
}
text_log.Print(")\n");
@@ -5085,10 +5566,12 @@ unsigned int ON_SubDLevel::DumpTopology(
// fEdges[face_edge_count] = { +eA, -eB, +eC, ...}
// fVertices[face_edge_count] = { vP, vQ, vR, ... }
//
+ damaged_face_count = 0U;
face_count = 0;
unsigned int face_dump_count = 0;
ON_2udex skipped_face_id = ON_2udex::Zero;
unsigned int max_face_id = 0;
+ bSkippedPreviousComponent = false;
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
{
if (face_count >= m_face_count && f->SubdivisionLevel() != level_index)
@@ -5097,7 +5580,11 @@ unsigned int ON_SubDLevel::DumpTopology(
max_face_id = f->m_id;
face_count++;
- if (bFaceIdTest)
+ const bool bIsDamaged = f->m_status.IsDamaged();
+ if (bIsDamaged)
+ ++damaged_face_count;
+
+ if (bFaceIdTest && (false == bIsDamaged || damaged_face_count > damaged_id_list_capacity))
{
bool bSkip = true;
for (;;)
@@ -5128,6 +5615,7 @@ unsigned int ON_SubDLevel::DumpTopology(
skipped_face_id.i = f->m_id;
else if (f->m_id > skipped_face_id.j)
skipped_face_id.j = f->m_id;
+ bSkippedPreviousComponent = true;
continue;
}
}
@@ -5137,10 +5625,25 @@ unsigned int ON_SubDLevel::DumpTopology(
face_dump_count++;
ON_TextLogIndent eindent(text_log);
- text_log.Print(
- "f%u:\n",
- f->m_id
- );
+ if (bSkippedPreviousComponent)
+ {
+ text_log.Print(L"...\n");
+ bSkippedPreviousComponent = false;
+ }
+ if (bIsDamaged)
+ {
+ text_log.Print(
+ "f%u (DAMAGED):\n",
+ f->m_id
+ );
+ }
+ else
+ {
+ text_log.Print(
+ "f%u:\n",
+ f->m_id
+ );
+ }
text_log.PushIndent();
@@ -5683,10 +6186,10 @@ class ON_SubDEdge* ON_SubDimple::AddEdge(
double v1_sector_weight
)
{
- if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight,true) )
+ if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_sector_weight,true) )
return ON_SUBD_RETURN_ERROR(nullptr);
- if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight,true) )
+ if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v1_sector_weight,true) )
return ON_SUBD_RETURN_ERROR(nullptr);
if ( nullptr != v0 && nullptr != v1 && v0->SubdivisionLevel() != v1->SubdivisionLevel() )
@@ -5695,25 +6198,25 @@ class ON_SubDEdge* ON_SubDimple::AddEdge(
const bool bEdgeTagSet = ON_SubD::EdgeTagIsSet(edge_tag);
if ( bEdgeTagSet
- && ON_SubDSectorType::IgnoredSectorWeight != v0_sector_weight
- && ON_SubDSectorType::UnsetSectorWeight != v0_sector_weight
+ && ON_SubDSectorType::IgnoredSectorCoefficient != v0_sector_weight
+ && ON_SubDSectorType::UnsetSectorCoefficient != v0_sector_weight
&& nullptr != v0
&& ON_SubD::VertexTag::Smooth == v0->m_vertex_tag
)
{
// minimizes checking when building subds because constant crease weights can be passed in
- v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
}
if ( bEdgeTagSet
- && ON_SubDSectorType::IgnoredSectorWeight != v1_sector_weight
- && ON_SubDSectorType::UnsetSectorWeight != v1_sector_weight
+ && ON_SubDSectorType::IgnoredSectorCoefficient != v1_sector_weight
+ && ON_SubDSectorType::UnsetSectorCoefficient != v1_sector_weight
&& nullptr != v1
&& ON_SubD::VertexTag::Smooth == v1->m_vertex_tag
)
{
// minimizes checking when building subds because constant crease weights can be passed in
- v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
}
class ON_SubDEdge* e = AllocateEdge(edge_tag);
@@ -5890,9 +6393,9 @@ class ON_SubDEdge* ON_SubD::AddEdge(
return AddEdgeWithSectorCoefficients(
edge_tag,
v0,
- ON_SubDSectorType::UnsetSectorWeight,
+ ON_SubDSectorType::UnsetSectorCoefficient,
v1,
- ON_SubDSectorType::UnsetSectorWeight
+ ON_SubDSectorType::UnsetSectorCoefficient
);
}
@@ -6045,14 +6548,14 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo
)
return false;
}
- m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
if (ON_SubD::EdgeTag::Smooth == m_edge_tag || ON_SubD::EdgeTag::SmoothX == m_edge_tag)
{
const unsigned int tagged_end_index = TaggedEndIndex();
if (tagged_end_index < 2)
{
- m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorWeight();
+ m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::Create( this, tagged_end_index).SectorCoefficient();
}
else if (2 == tagged_end_index)
{
@@ -6063,8 +6566,8 @@ bool ON_SubDEdge::UpdateEdgeSectorCoefficientsForExperts(bool bUnsetEdgeSectorCo
m_edge_tag = ON_SubD::EdgeTag::Crease;
else if (ON_SubD::EdgeTag::SmoothX == m_edge_tag)
{
- m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorWeight();
- m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorWeight();
+ m_sector_coefficient[0] = ON_SubDSectorType::Create( this, 0).SectorCoefficient();
+ m_sector_coefficient[1] = ON_SubDSectorType::Create( this, 1).SectorCoefficient();
}
}
}
@@ -6179,7 +6682,7 @@ class ON_SubDFace* ON_SubD::AddFace(
}
ON_SubDFace* face
- = (eptr[edge_count - 1].IsNull() && (eptr[0].RelativeVertex(0) != eptr[edge_count - 1].RelativeVertex(1)))
+ = (eptr[edge_count - 1].IsNotNull() && eptr[0].RelativeVertex(0) == eptr[edge_count - 1].RelativeVertex(1))
? AddFace(eptr, edge_count)
: nullptr;
onfree(eptr);
@@ -6390,13 +6893,22 @@ bool ON_SubD::RemoveFaceConnections(
static bool ON_SubDFace_GetSubdivisionPointError(
const class ON_SubDFace* face,
- double face_point[3],
+ double subdivision_point[3],
bool bDamagedState
)
{
- if (nullptr == face || nullptr == face_point)
- return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - edge is not necessarily damaged
+ if (nullptr == subdivision_point)
+ return ON_SUBD_RETURN_ERROR(false); // caller passed a null pointer - face is not necessarily damaged
+ // make sure returned point is not used by a caller who doesn't bother to check return codes.
+ subdivision_point[0] = ON_DBL_QNAN;
+ subdivision_point[1] = ON_DBL_QNAN;
+ subdivision_point[2] = ON_DBL_QNAN;
+
+ if (nullptr == face)
+ return ON_SUBD_RETURN_ERROR(false);
+
+ // face is damaged in some way - mark it
face->m_status.SetDamagedState(bDamagedState);
return ON_SUBD_RETURN_ERROR(false);
@@ -6452,12 +6964,16 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
// special treatment will run noticably faster.
e_ptr = edge_ptr[0].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
+ if ( nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] )
+ return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
vertexP[1] = e->m_vertex[1 - edir]->m_P;
e_ptr = edge_ptr[2].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
+ if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
+ return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[2] = e->m_vertex[edir]->m_P;
vertexP[3] = e->m_vertex[1 - edir]->m_P;
@@ -6515,6 +7031,8 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
{
e_ptr = edge_ptr[i].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
+ if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
+ return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
vertexP[1] = e->m_vertex[1 - edir]->m_P;
@@ -6533,6 +7051,8 @@ bool ON_SubDFace::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
// odd number of edges and vertices
e_ptr = edge_ptr[count - 1].m_ptr;
e = ON_SUBD_EDGE_POINTER(e_ptr);
+ if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1])
+ return ON_SubDFace_GetSubdivisionPointError(this, subdivision_point, true);
edir = ON_SUBD_EDGE_DIRECTION(e_ptr);
vertexP[0] = e->m_vertex[edir]->m_P;
faceP[0] += vertexP[0][0];
@@ -6831,11 +7351,13 @@ void ON_SubDVertex::VertexModifiedNofification() const
for (unsigned short vei = 0; vei < m_edge_count; vei++)
{
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(m_edges[vei].m_ptr);
- if (nullptr != edge)
- {
- edge->ClearSavedSubdivisionPoints();
- edge->UnsetSectorCoefficientsForExperts();
- }
+ if (nullptr == edge)
+ continue;
+ edge->ClearSavedSubdivisionPoints();
+ edge->UnsetSectorCoefficientsForExperts();
+ const ON_SubDVertex* v1 = edge->m_vertex[1-ON_SUBD_EDGE_DIRECTION(m_edges[vei].m_ptr)];
+ if (nullptr != v1)
+ v1->ClearSavedSubdivisionPoints();
}
// This is needed to clear cached information in the Catmull-Clark
@@ -6856,7 +7378,6 @@ void ON_SubDEdge::EdgeModifiedNofification() const
UnsetSectorCoefficientsForExperts();
for (unsigned int evi = 0; evi < 2; evi++)
{
- const_cast(this)->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight;
if (nullptr != m_vertex[evi])
m_vertex[evi]->VertexModifiedNofification();
}
@@ -6882,8 +7403,8 @@ void ON_SubDEdge::EdgeModifiedNofification() const
void ON_SubDEdge::UnsetSectorCoefficientsForExperts() const
{
- const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
- const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ const_cast(this)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
+ const_cast(this)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
}
void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge_end_dex) const
@@ -6899,11 +7420,11 @@ void ON_SubDVertex::UnsetSectorCoefficientsForExperts(unsigned int relative_edge
? ((0 == edir ? false : true) == (0 == relative_edge_end_dex ? false : true) ? 0U : 1U)
: 2U;
if ( evi < 2)
- e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorWeight;
+ e->m_sector_coefficient[evi] = ON_SubDSectorType::UnsetSectorCoefficient;
else
{
- e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
- e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ e->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
+ e->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
}
}
}
@@ -6949,7 +7470,6 @@ void ON_SubDComponentBase::Internal_ClearSubdivisionPointAndSurfacePointFlags()
{
ON_SUBD_CACHE_CLEAR_POINT_FLAG(m_saved_points_flags);
ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags);
- ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
}
bool ON_SubDComponentBase::Internal_SubdivisionPointFlag() const
@@ -6967,20 +7487,9 @@ bool ON_SubDComponentBase::Internal_SurfacePointFlag() const
return (0 != ON_SUBD_CACHE_LIMITLOC_FLAG(m_saved_points_flags));
}
-bool ON_SubDComponentBase::Internal_ControlNetFragmentFlag() const
-{
- return (0 != ON_SUBD_CACHE_CTRLNETFRAG_FLAG(m_saved_points_flags));
-}
-
void ON_SubDComponentBase::Internal_ClearSurfacePointFlag() const
{
ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(m_saved_points_flags);
- ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
-}
-
-void ON_SubDComponentBase::Internal_ClearControlNetFragmentFlag() const
-{
- ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(m_saved_points_flags);
}
bool ON_SubDComponentBase::SavedSubdivisionPointIsSet() const
@@ -7074,12 +7583,30 @@ void ON_SubDComponentBase::Internal_SetSavedSurfacePointFlag(bool bSavedSurfaceP
Internal_ClearSurfacePointFlag();
}
-void ON_SubDComponentBase::Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const
+
+void ON_SubDComponentBase::Internal_SetModified1Flag() const
{
- if (bSavedControlNetFragmentFlag)
- m_saved_points_flags |= ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT;
- else
- Internal_ClearSurfacePointFlag();
+ m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit;
+}
+
+void ON_SubDComponentBase::Internal_SetModified2Flag() const
+{
+ m_saved_points_flags |= ON_SubDComponentBase::ModifiedFlags::Modified1Bit;
+}
+
+void ON_SubDComponentBase::Internal_ClearModifiedFlags() const
+{
+ m_saved_points_flags &= ~ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask;
+}
+
+bool ON_SubDComponentBase::Internal_Modified1IsSet() const
+{
+ return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::Modified1Bit));
+}
+
+bool ON_SubDComponentBase::Internal_Modified1or2IsSet() const
+{
+ return (0 != (m_saved_points_flags & ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask));
}
bool ON_SubDFace::ReverseEdgeList()
@@ -7335,6 +7862,7 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
const bool bApplyDisplacement = GetSubdivisionDisplacement(displacementV);
const double* edgeP[2] = { edge_vertex[0]->m_P, edge_vertex[1]->m_P };
+ const double edgePsum[3] = { edgeP[0][0] + edgeP[1][0], edgeP[0][1] + edgeP[1][1], edgeP[0][2] + edgeP[1][2] };
if ( IsSmooth() )
{
@@ -7361,7 +7889,7 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
= (ON_SubD::VertexTag::Smooth != edge_vertex[0]->m_vertex_tag)
? 0
: ((ON_SubD::VertexTag::Smooth != edge_vertex[1]->m_vertex_tag) ? 1 : ON_UNSET_UINT_INDEX);
- double edgePsum[3];
+ double EP[3];
if (
ON_UNSET_UINT_INDEX == tagged_end
|| 0.5 == m_sector_coefficient[tagged_end]
@@ -7369,9 +7897,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
)
{
// ignore edge weights
- edgePsum[0] = 0.375*(edgeP[0][0] + edgeP[1][0]);
- edgePsum[1] = 0.375*(edgeP[0][1] + edgeP[1][1]);
- edgePsum[2] = 0.375*(edgeP[0][2] + edgeP[1][2]);
+ EP[0] = 0.375*edgePsum[0];
+ EP[1] = 0.375*edgePsum[1];
+ EP[2] = 0.375*edgePsum[2];
}
else if (ON_SubD::VertexTag::Smooth == edge_vertex[1 - tagged_end]->m_vertex_tag
&& m_sector_coefficient[tagged_end] > 0.0
@@ -7381,9 +7909,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
double w[2];
w[tagged_end] = m_sector_coefficient[tagged_end];
w[1 - tagged_end] = 1.0 - w[tagged_end];
- edgePsum[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]);
- edgePsum[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]);
- edgePsum[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]);
+ EP[0] = 0.75*(w[0] * edgeP[0][0] + w[1] * edgeP[1][0]);
+ EP[1] = 0.75*(w[0] * edgeP[0][1] + w[1] * edgeP[1][1]);
+ EP[2] = 0.75*(w[0] * edgeP[0][2] + w[1] * edgeP[1][2]);
}
else
{
@@ -7397,9 +7925,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
if (4 == face_edge_count[0] && 4 == face_edge_count[1])
{
// common case when both neighboring faces are quads
- subdivision_point[0] = edgePsum[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]);
- subdivision_point[1] = edgePsum[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]);
- subdivision_point[2] = edgePsum[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]);
+ subdivision_point[0] = EP[0] + 0.0625*(facePsum[0][0] + facePsum[1][0]);
+ subdivision_point[1] = EP[1] + 0.0625*(facePsum[0][1] + facePsum[1][1]);
+ subdivision_point[2] = EP[2] + 0.0625*(facePsum[0][2] + facePsum[1][2]);
if (bApplyDisplacement)
{
@@ -7414,9 +7942,14 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
if (3 == face_edge_count[0] && 3 == face_edge_count[1])
{
// common case when both neighboring faces are triangles
- subdivision_point[0] = edgePsum[0] + 0.125*(facePsum[0][0] + facePsum[1][0]);
- subdivision_point[1] = edgePsum[1] + 0.125*(facePsum[0][1] + facePsum[1][1]);
- subdivision_point[2] = edgePsum[2] + 0.125*(facePsum[0][2] + facePsum[1][2]);
+
+ //// bug in evaluation prior to Nov 11, 2019
+ ////subdivision_point[0] = EP[0] + 0.125*(facePsum[0][0] + facePsum[1][0]);
+ ////subdivision_point[1] = EP[1] + 0.125*(facePsum[0][1] + facePsum[1][1]);
+ ////subdivision_point[2] = EP[2] + 0.125*(facePsum[0][2] + facePsum[1][2]);
+ subdivision_point[0] = EP[0] + (0.5*edgePsum[0] + facePsum[0][0] + facePsum[1][0]) / 12.0;
+ subdivision_point[1] = EP[1] + (0.5*edgePsum[1] + facePsum[0][1] + facePsum[1][1]) / 12.0;
+ subdivision_point[2] = EP[2] + (0.5*edgePsum[2] + facePsum[0][2] + facePsum[1][2]) / 12.0;
if (bApplyDisplacement)
{
@@ -7429,11 +7962,19 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
}
// general formula works for all cases including face_edge_count[0] != face_count[2]
- const double f0 = 0.125 / ((double)(face_edge_count[0]-2));
- const double f1 = 0.125 / ((double)(face_edge_count[1]-2));
- subdivision_point[0] = edgePsum[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0];
- subdivision_point[1] = edgePsum[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1];
- subdivision_point[2] = edgePsum[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2];
+ //// bug in evaluation prior to Nov 11, 2019
+ ////const double f0 = 0.125 / ((double)(face_edge_count[0] - 2));
+ ////const double f1 = 0.125 / ((double)(face_edge_count[1] - 2));
+ ////subdivision_point[0] = EP[0] + f0 * facePsum[0][0] + f1 * facePsum[1][0];
+ ////subdivision_point[1] = EP[1] + f0 * facePsum[0][1] + f1 * facePsum[1][1];
+ ////subdivision_point[2] = EP[2] + f0 * facePsum[0][2] + f1 * facePsum[1][2];
+
+ const double f0 = (double)(face_edge_count[0] * 4U);
+ const double f1 = (double)(face_edge_count[1] * 4U);
+ const double x = 1.0 / f0 + 1.0 / f1 - 0.125;
+ subdivision_point[0] = EP[0] + x * edgePsum[0] + facePsum[0][0] / f0 + facePsum[1][0] / f1;
+ subdivision_point[1] = EP[1] + x * edgePsum[1] + facePsum[0][1] / f0 + facePsum[1][1] / f1;
+ subdivision_point[2] = EP[2] + x * edgePsum[2] + facePsum[0][2] / f0 + facePsum[1][2] / f1;
if (bApplyDisplacement)
{
@@ -7447,9 +7988,9 @@ bool ON_SubDEdge::EvaluateCatmullClarkSubdivisionPoint(double subdivision_point[
if ( IsCrease() )
{
- subdivision_point[0] = 0.5*(edgeP[0][0] + edgeP[1][0]);
- subdivision_point[1] = 0.5*(edgeP[0][1] + edgeP[1][1]);
- subdivision_point[2] = 0.5*(edgeP[0][2] + edgeP[1][2]);
+ subdivision_point[0] = 0.5*edgePsum[0];
+ subdivision_point[1] = 0.5*edgePsum[1];
+ subdivision_point[2] = 0.5*edgePsum[2];
if (bApplyDisplacement)
{
@@ -7676,22 +8217,22 @@ bool ON_SubDSectorSurfacePoint::IsSet(
return false;
}
- if (false == bUndefinedNormalIsPossible)
+ p = m_limitT1;
+ p1 = p + 6;
+ while (p < p1)
{
- p = m_limitT1;
- p1 = p + 6;
- while (p < p1)
+ const double* p2 = p + 3;
+ y = 0.0;
+ while (p < p2)
+ {
+ x = *p++;
+ if (ON_UNSET_VALUE == x || !(x == x))
+ return false;
+ if (0.0 != x)
+ y = x;
+ }
+ if (false == bUndefinedNormalIsPossible)
{
- const double* p2 = p + 3;
- y = 0.0;
- while (p < p2)
- {
- x = *p++;
- if (ON_UNSET_VALUE == x || !(x == x))
- return false;
- if (0.0 != x)
- y = x;
- }
if (!(y != 0.0))
return false;
}
@@ -7706,8 +8247,11 @@ bool ON_SubDSectorSurfacePoint::IsSet(
return false;
y += x * x;
}
- if (!(fabs(y - 1.0) <= 1e-4))
- return false;
+ if (false == bUndefinedNormalIsPossible)
+ {
+ if (!(fabs(y - 1.0) <= 1e-4))
+ return false;
+ }
}
return true;
@@ -8455,7 +8999,7 @@ bool ON_SubDimple::LocalSubdivide(
radial_edges.Reserve(e_count /2);
for (unsigned fei = 0; fei < e_count; fei += 2)
{
- ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorWeight, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorWeight);
+ ON_SubDEdge* r = AddEdge(ON_SubD::EdgeTag::Smooth, center, ON_SubDSectorType::UnsetSectorCoefficient, const_cast(eptrs[fei].RelativeVertex(1)), ON_SubDSectorType::UnsetSectorCoefficient);
radial_edges.Append(r);
}
@@ -8645,7 +9189,7 @@ unsigned int ON_SubDimple::Internal_GlobalQuadSubdivideFace(
unsigned int f1_count = 0;
- const double w_2facesector = ON_SubDSectorType::CreaseSectorWeight(2);
+ const double w_2facesector = ON_SubDSectorType::CreaseSectorCoefficient(2);
for (unsigned int i = 0; i < f0_edge_count; i++)
{
@@ -8808,12 +9352,12 @@ bool ON_SubDimple::GlobalSubdivide(
return true;
}
-ON_SubDEdgePtr ON_SubDimple::MergeEdges(
+ON_SubDEdgePtr ON_SubDimple::MergeConsecutiveEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
- if ( false == ON_SubD::EdgesCanBeMerged(eptr0,eptr1) )
+ if ( false == ON_SubD::EdgesAreConsecutive(eptr0,eptr1) )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
ON_SubDEdge* e[2] = { ON_SUBD_EDGE_POINTER(eptr0.m_ptr), ON_SUBD_EDGE_POINTER(eptr1.m_ptr) };
@@ -8887,19 +9431,19 @@ ON_SubDEdgePtr ON_SubDimple::MergeEdges(
? ON_SubD::EdgeTag::SmoothX
: ON_SubD::EdgeTag::Smooth;
if ( false == bTagged[0])
- e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
+ e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
else if (!(e[0]->m_sector_coefficient[0] > 0.0 && e[0]->m_sector_coefficient[0] < 1.0))
- e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
+ e[0]->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
if ( false == bTagged[1])
- e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
else if (!(e[0]->m_sector_coefficient[1] > 0.0 && e[0]->m_sector_coefficient[1] < 1.0))
- e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ e[0]->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
}
else
{
e[0]->m_edge_tag = ON_SubD::EdgeTag::Crease;
- e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ e[0]->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ e[0]->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
}
ReturnEdge(e[1]);
@@ -8907,16 +9451,16 @@ ON_SubDEdgePtr ON_SubDimple::MergeEdges(
return eptr0;
}
-ON_SubDEdgePtr ON_SubD::MergeEdges(
+ON_SubDEdgePtr ON_SubD::MergeConsecutiveEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
ON_SubDimple* subdimple = SubDimple(false);
- return (nullptr != subdimple) ? subdimple->MergeEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null;
+ return (nullptr != subdimple) ? subdimple->MergeConsecutiveEdges(eptr0, eptr1) : ON_SubDEdgePtr::Null;
}
-static bool EdgesAreMergableTest(
+static bool Internal_EdgesAreConsecutive(
const ON_SubDEdgePtr eptr[2],
bool bTestColinearity,
double distance_tolerance,
@@ -9051,13 +9595,13 @@ static bool EdgesAreMergableTest(
return true;
}
-bool ON_SubD::EdgesCanBeMerged(
+bool ON_SubD::EdgesAreConsecutive(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
)
{
ON_SubDEdgePtr eptr[2] = { eptr0,eptr1 };
- return EdgesAreMergableTest(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN);
+ return Internal_EdgesAreConsecutive(eptr,false,ON_DBL_QNAN,ON_DBL_QNAN,ON_DBL_QNAN);
}
static bool Internal_EdgesPassTypeFilter(
@@ -9115,7 +9659,7 @@ unsigned int ON_SubDimple::MergeColinearEdges(
{
eptr[0] = eptr[1];
eptr[1] = f->EdgePtr(fei0);
- if (false == EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance))
+ if (false == Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance))
break;
if (false == Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges))
break;
@@ -9143,14 +9687,14 @@ unsigned int ON_SubDimple::MergeColinearEdges(
eptr[0] = eptr[1];
eptr[1] = f->EdgePtr(fei);
if (
- EdgesAreMergableTest(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)
+ Internal_EdgesAreConsecutive(eptr, true, distance_tolerance, maximum_aspect, sin_angle_tolerance)
&& Internal_EdgesPassTypeFilter(eptr, bMergeBoundaryEdges, bMergeInteriorCreaseEdges, bMergeInteriorSmoothEdges)
)
{
// merge edges f->Edge(fei-1) and f->Edge(fei) into f->Edge(fei-1).
- if (eptr[0].m_ptr != MergeEdges(eptr[0], eptr[1]).m_ptr)
+ if (eptr[0].m_ptr != MergeConsecutiveEdges(eptr[0], eptr[1]).m_ptr)
{
- ON_SUBD_ERROR("Bug in edge merging.");
+ ON_SUBD_ERROR("Bug in consecutive edge merging.");
break;
}
++removed_edge_count;
@@ -9319,6 +9863,13 @@ const class ON_SubDVertex* ON_SubD::VertexFromId(
return nullptr;
}
+const class ON_SubDVertex* ON_SubD::VertexFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+) const
+{
+ return (ON_COMPONENT_INDEX::TYPE::subd_vertex == component_index.m_type) ? VertexFromId(component_index.m_index) : nullptr;
+}
+
const ON_COMPONENT_INDEX ON_SubDVertex::ComponentIndex() const
{
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_vertex,m_id);
@@ -9380,6 +9931,13 @@ const class ON_SubDEdge* ON_SubD::EdgeFromId(
return nullptr;
}
+const class ON_SubDEdge* ON_SubD::EdgeFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+) const
+{
+ return (ON_COMPONENT_INDEX::TYPE::subd_edge == component_index.m_type) ? EdgeFromId(component_index.m_index) : nullptr;
+}
+
const ON_COMPONENT_INDEX ON_SubDEdge::ComponentIndex() const
{
return ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::TYPE::subd_edge,m_id);
@@ -9442,6 +10000,12 @@ const class ON_SubDFace* ON_SubD::FaceFromId(
return nullptr;
}
+const class ON_SubDFace* ON_SubD::FaceFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+) const
+{
+ return (ON_COMPONENT_INDEX::TYPE::subd_face == component_index.m_type) ? FaceFromId(component_index.m_index) : nullptr;
+}
const ON_COMPONENT_INDEX ON_SubDFace::ComponentIndex() const
{
@@ -9904,8 +10468,8 @@ const ON_SubDEdge* ON_SubDimple::SplitEdge(
// Either edge was a crease, new_edge is a crease, and sector weights do not applie
// or edge was X or Smooth, edge is smooth, new_edge is smooth, new_vertex is smooth,
// and the sector weights at this vertex do not apply.
- edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
- new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ new_edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
AddVertexToLevel(new_vertex);
AddEdgeToLevel(new_edge);
@@ -10024,8 +10588,8 @@ const ON_SubDEdge* ON_SubDimple::SplitFace(
new_e = AddEdge(
((v[0]->IsSmooth() || v[1]->IsSmooth()) ? ON_SubD::EdgeTag::Smooth : ON_SubD::EdgeTag::SmoothX),
- v[0], ON_SubDSectorType::UnsetSectorWeight,
- v[1], ON_SubDSectorType::UnsetSectorWeight);
+ v[0], ON_SubDSectorType::UnsetSectorCoefficient,
+ v[1], ON_SubDSectorType::UnsetSectorCoefficient);
if (nullptr == new_e)
break;
@@ -10519,7 +11083,7 @@ void ON_SubDLevel::ClearEvaluationCache() const
continue;
// corner sector coefficients depend on the subtended angle of the sector's crease boundary.
// All other sector coefficients are independent of vertex location.
- const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorWeight();
+ const_cast(edge)->m_sector_coefficient[evi] = ON_SubDSectorType::Create(edge, evi).SectorCoefficient();
}
}
}
@@ -10530,6 +11094,85 @@ void ON_SubDLevel::ClearEvaluationCache() const
}
}
+void ON_SubDLevel::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex0, bool bTagChanged ) const
+{
+ ClearEdgeFlags();
+ ClearBoundingBox();
+ m_surface_mesh = ON_SubDMesh::Empty;
+ m_control_net_mesh = ON_SubDMesh::Empty;
+
+ if (nullptr == vertex0)
+ return;
+
+ vertex0->ClearSavedSubdivisionPoints();
+
+ for (unsigned short v0ei = 0; v0ei < vertex0->m_edge_count; ++v0ei)
+ {
+ const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(vertex0->m_edges[v0ei].m_ptr);
+ if (nullptr == edge0)
+ continue;
+ edge0->ClearSavedSubdivisionPoints();
+ if (bTagChanged)
+ edge0->UnsetSectorCoefficientsForExperts();
+ else if (edge0->IsSmooth())
+ {
+ for (unsigned evi = 0; evi < 2; evi++)
+ {
+ if (false == (edge0->m_sector_coefficient[evi] > 0.0 && edge0->m_sector_coefficient[evi] < 1.0))
+ continue;
+ const ON_SubDVertex* v = edge0->m_vertex[evi];
+ if (nullptr == v)
+ continue;
+ if (ON_SubD::VertexTag::Corner != v->m_vertex_tag)
+ continue;
+ // corner sector coefficients depend on the subtended angle of the sector's crease boundary.
+ // All other sector coefficients are independent of vertex location.
+ edge0->UnsetSectorCoefficientsForExperts();
+ }
+ }
+ }
+
+ for (unsigned short v0fi = 0; v0fi < vertex0->m_face_count; ++v0fi)
+ {
+ const ON_SubDFace* face0 = vertex0->m_faces[v0fi];
+ if (nullptr == face0)
+ continue;
+ face0->ClearSavedSubdivisionPoints();
+ const ON_SubDEdgePtr* face0_eptr = face0->m_edge4;
+ for (unsigned short f0ei = 0; f0ei < face0->m_edge_count; ++f0ei, ++face0_eptr)
+ {
+ if (4 == f0ei)
+ {
+ face0_eptr = face0->m_edgex;
+ if (nullptr == face0_eptr)
+ break;
+ }
+ const ON_SubDEdge* edge0 = ON_SUBD_EDGE_POINTER(face0_eptr->m_ptr);
+ if (nullptr == edge0)
+ continue;
+ edge0->ClearSavedSubdivisionPoints();
+ const ON_SubDVertex* vertex1 = edge0->m_vertex[ON_SUBD_EDGE_DIRECTION(face0_eptr->m_ptr)];
+ if (vertex0 == vertex1 || nullptr == vertex1)
+ continue;
+ vertex1->ClearSavedSubdivisionPoints();
+ for (unsigned short v1fi = 0; v1fi < vertex1->m_face_count; ++v1fi)
+ {
+ const ON_SubDFace* face1 = vertex1->m_faces[v1fi];
+ if (nullptr == face1 || face0 == face1)
+ continue;
+ face1->ClearSavedSubdivisionPoints();
+ }
+ for (unsigned short v1ei = 0; v1ei < vertex1->m_edge_count; ++v1ei)
+ {
+ const ON_SubDEdge* edge1 = ON_SUBD_EDGE_POINTER(vertex1->m_edges[v1ei].m_ptr);
+ if (nullptr == edge1)
+ continue;
+ edge1->ClearSavedSubdivisionPoints();
+ }
+ }
+ }
+}
+
unsigned int ON_SubD::ComponentPtrFromComponentIndex(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
@@ -10822,20 +11465,20 @@ unsigned int ON_SubDLevel::UpdateEdgeTags(
if (2 != edge->m_face_count)
{
edge->m_edge_tag = ON_SubD::EdgeTag::Crease;
- edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
}
else
{
- edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
const bool bBothVertexTagsAreSet
= ON_SubD::VertexTag::Unset != edge->m_vertex[0]->m_vertex_tag
&& ON_SubD::VertexTag::Unset != edge->m_vertex[1]->m_vertex_tag
;
const unsigned int tagged_end_index = edge->TaggedEndIndex();
if (0 == tagged_end_index || 1 == tagged_end_index)
- edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::IgnoredSectorCoefficient;
switch (edge_tag0)
{
@@ -10849,8 +11492,8 @@ unsigned int ON_SubDLevel::UpdateEdgeTags(
edge->m_edge_tag = ON_SubD::EdgeTag::Smooth;
if (3 == tagged_end_index)
{
- edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
}
}
break;
@@ -10862,14 +11505,14 @@ unsigned int ON_SubDLevel::UpdateEdgeTags(
}
else if (3 == tagged_end_index && bBothVertexTagsAreSet)
{
- edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
}
break;
case ON_SubD::EdgeTag::Crease:
- edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::IgnoredSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::IgnoredSectorCoefficient;
break;
//case ON_SubD::EdgeTag::Sharp:
@@ -11203,11 +11846,13 @@ unsigned int ON_SubDimple::DeleteComponents(
deleted_component_count++;
}
+ ON_ComponentStatus allsetcheck;
ON_SubDEdge* next_edge = level->m_edge[0];
for (ON_SubDEdge* edge = next_edge; nullptr != edge; edge = next_edge)
{
next_edge = const_cast< ON_SubDEdge* >(edge->m_next_edge);
- bool bDelete = (ON_ComponentStatus::AllSet == edge->m_status || (bDeleteIsolatedEdges && 0 == edge->m_face_count) );
+ allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, edge->m_status);
+ bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == edge->m_face_count) );
if (false == bDelete)
{
for (unsigned short evi = 0; evi < 2 && false == bDelete; evi++)
@@ -11232,7 +11877,8 @@ unsigned int ON_SubDimple::DeleteComponents(
for (ON_SubDVertex* vertex = next_vertex; nullptr != vertex; vertex = next_vertex)
{
next_vertex = const_cast(vertex->m_next_vertex);
- bool bDelete = (ON_ComponentStatus::AllSet == vertex->m_status || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count );
+ allsetcheck = ON_ComponentStatus::LogicalAnd(ON_ComponentStatus::AllSet, vertex->m_status);
+ bool bDelete = (ON_ComponentStatus::AllSet == allsetcheck || (bDeleteIsolatedEdges && 0 == vertex->m_face_count) || 0 == vertex->m_edge_count );
if ( false == bDelete )
continue;
@@ -11282,8 +11928,8 @@ unsigned int ON_SubDimple::DeleteComponents(
if (edge->m_face_count != 2)
edge->m_edge_tag = ON_SubD::EdgeTag::Crease;
- edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
- edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ edge->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
+ edge->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
}
}
@@ -11313,13 +11959,6 @@ unsigned int ON_SubDimple::DeleteComponents(
vertex->m_edges[vertex->m_edge_count++] = vertex->m_edges[vei];
}
- if ( crease_count > 2 )
- vertex->m_vertex_tag = ON_SubD::VertexTag::Corner;
- else if (false == bInteriorVertex || crease_count > 1)
- {
- if (false == vertex->IsCreaseOrCorner())
- vertex->m_vertex_tag = ON_SubD::VertexTag::Crease;
- }
count = vertex->m_face_count;
vertex->m_face_count = 0;
@@ -11337,6 +11976,18 @@ unsigned int ON_SubDimple::DeleteComponents(
m_heap.ReturnVertex(vertex);
deleted_component_count++;
}
+ else
+ {
+ if (1 == crease_count && 1 == vertex->m_edge_count && 0 == vertex->m_face_count)
+ vertex->m_vertex_tag = ON_SubD::VertexTag::Corner;
+ else if (crease_count > 2)
+ vertex->m_vertex_tag = ON_SubD::VertexTag::Corner;
+ else if (false == bInteriorVertex || crease_count > 1)
+ {
+ if (false == vertex->IsCreaseOrCorner())
+ vertex->m_vertex_tag = ON_SubD::VertexTag::Crease;
+ }
+ }
}
if (0 == level->m_vertex_count || 0 == level->m_edge_count || (bDeleteIsolatedEdges && 0 == level->m_face_count))
@@ -11671,10 +12322,10 @@ unsigned int ON_SubD::SetComponentStatus(
}
ON_SubDComponentMarksClearAndRestore::ON_SubDComponentMarksClearAndRestore(
- ON_SubD& subd
+ const ON_SubD& subd
)
{
- m_subd.ShareContentsFrom(subd);
+ m_subd.ShareDimple(subd);
m_subd.ClearComponentMarks(true, true, true, &m_component_list);
}
@@ -13811,15 +14462,26 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(
if (bIsSmooth != (bIsCrease?false:true))
break;
- if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type)
+ const unsigned short vertex_edge_count
+ = ((ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type)
+ && (1 == edge->m_face_count || 2 == edge->m_face_count))
+ ? (edge->m_face_count + 2)
+ : ((unsigned short)0);
+
+ if (vertex_edge_count > 0 && vertex_edge_count != v->m_edge_count)
+ break;
+
+ if (ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type)
{
if (bIsSmooth)
{
+ // edge is smooth so vertex must be smooth
if (ON_SubD::VertexTag::Smooth != v->m_vertex_tag)
break;
}
else
{
+ // edge is crease so vertex must be crease
if (ON_SubD::VertexTag::Crease != v->m_vertex_tag)
break;
}
@@ -13892,7 +14554,13 @@ const ON_SubDEdgePtr ON_SubDEdgeChain::EdgeChainNeighbor(
{
if (bIsSmooth != nxt->IsSmooth())
{
- if ( ON_SubD::ChainType::EqualEdgeTag == chain_type || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type)
+ // edge tag changed
+ if (
+ ON_SubD::ChainType::EqualEdgeTag == chain_type
+ || ON_SubD::ChainType::EqualEdgeAndVertexTag == chain_type
+ || ON_SubD::ChainType::EqualEdgeTagAndOrdinary == chain_type
+ || ON_SubD::ChainType::EqualEdgeAndVertexTagAndOrdinary == chain_type
+ )
break;
}
if (false == bEnableStatusCheck || ON_ComponentStatus::StatusCheck(nxt->m_status, status_pass, status_fail))
@@ -14897,6 +15565,37 @@ void ON_SubDEdgePtrLink::Resolve3OrMoreEdges(
return;
}
+unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
+ const ON_SubD& subd,
+ const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges,
+ unsigned int minimum_chain_length,
+ ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
+)
+{
+ const unsigned count = unsorted_edges.UnsignedCount();
+ ON_SimpleArray< const ON_SubDEdge* > a(count);
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_SubDEdge* e = subd.EdgeFromComponentIndex(unsorted_edges[i]);
+ if (nullptr != e)
+ a.Append(e);
+ }
+ return SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges);
+}
+
+unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
+ const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges,
+ unsigned int minimum_chain_length,
+ ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
+)
+{
+ const unsigned count = unsorted_edges.UnsignedCount();
+ ON_SimpleArray< const ON_SubDEdge* > a(count);
+ for (unsigned i = 0; i < count; ++i)
+ a.Append(unsorted_edges[i].Edge());
+ return ON_SubDEdgeChain::SortEdgesIntoEdgeChains(a, minimum_chain_length, sorted_edges);
+}
+
unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges,
unsigned int minimum_chain_length,
@@ -15190,6 +15889,866 @@ unsigned int ON_SubDEdgeChain::SortEdgesIntoEdgeChains(
return chain_count;
}
+unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(
+ const ON_SubD& subd,
+ const ON_SimpleArray< ON_COMPONENT_INDEX >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chain
+)
+{
+ const unsigned count = edges.UnsignedCount();
+ ON_SimpleArray< const ON_SubDEdge* > a(count);
+ for (unsigned i = 0; i < count; ++i)
+ a.Append(subd.EdgeFromComponentIndex(edges[i]));
+ return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain);
+}
+
+unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(
+ const ON_SimpleArray< ON_SubDComponentPtr >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chain
+)
+{
+ const unsigned count = edges.UnsignedCount();
+ ON_SimpleArray< const ON_SubDEdge* > a(count);
+ for (unsigned i = 0; i < count; ++i)
+ a.Append(edges[i].Edge());
+ return ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(a, edge_chain);
+}
+
+unsigned int ON_SubDEdgeChain::OrientEdgesIntoEdgeChains(
+ const ON_SimpleArray< const ON_SubDEdge* >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+)
+{
+ const unsigned count = edges.UnsignedCount();
+ edge_chains.SetCount(0);
+ edge_chains.Reserve(count);
+ unsigned int chain_count = 0;
+ unsigned chain_length = 0;
+ ON_SubDEdgePtr* prev_eptr = nullptr;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_SubDEdge* e = edges[i];
+ if (nullptr == e || nullptr == e->m_vertex[0] || nullptr == e->m_vertex[1] || e->m_vertex[0] == e->m_vertex[1])
+ continue;
+ ON_SubDEdgePtr& eptr = edge_chains.AppendNew();
+ eptr = ON_SubDEdgePtr::Create(e);
+ if (nullptr != prev_eptr && prev_eptr->RelativeVertex(1) != eptr.RelativeVertex(0) )
+ {
+ const ON_SubDVertex* prev_v[2] = { prev_eptr->RelativeVertex(0), prev_eptr->RelativeVertex(1) };
+ const ON_SubDVertex* v[2] = { eptr.RelativeVertex(0), eptr.RelativeVertex(1) };
+ if (prev_v[1] == v[1])
+ eptr = eptr.Reversed();
+ else if (1 == chain_length)
+ {
+ if (prev_v[0] == v[0])
+ *prev_eptr = prev_eptr->Reversed();
+ else if (prev_v[0] == v[1])
+ {
+ *prev_eptr = prev_eptr->Reversed();
+ eptr = eptr.Reversed();
+ }
+ else
+ prev_eptr = nullptr;
+ }
+ else
+ prev_eptr = nullptr;
+ }
+
+ if (nullptr == prev_eptr)
+ {
+ chain_count = 1;
+ chain_length = 0;
+ }
+ prev_eptr = &eptr;
+ ++chain_length;
+ }
+ return chain_count;
+}
+
+
+
+ON_SubDComponentList::ON_SubDComponentList(const ON_SubDComponentList& src)
+ : m_subd_runtime_serial_number(src.m_subd_runtime_serial_number)
+ , m_subd_content_serial_number(src.m_subd_content_serial_number)
+ , m_subd_vertex_count(src.m_subd_vertex_count)
+ , m_subd_edge_count(src.m_subd_edge_count)
+ , m_subd_face_count(src.m_subd_face_count)
+ , m_component_list(src.m_component_list)
+{
+ m_subd.ShareDimple(src.m_subd);
+}
+
+ON_SubDComponentList& ON_SubDComponentList::operator=(const ON_SubDComponentList& src)
+{
+ if (this != &src)
+ {
+ m_subd_runtime_serial_number = src.m_subd_runtime_serial_number;
+ m_subd_content_serial_number = src.m_subd_content_serial_number;
+ m_subd_vertex_count = src.m_subd_vertex_count;
+ m_subd_edge_count = src.m_subd_edge_count;
+ m_subd_face_count = src.m_subd_face_count;
+ m_component_list = src.m_component_list;
+ m_subd.ShareDimple(src.m_subd);
+ }
+ return *this;
+}
+
+ON__UINT64 ON_SubDComponentList::SubDRuntimeSerialNumber() const
+{
+ return m_subd_runtime_serial_number;
+}
+
+ON__UINT64 ON_SubDComponentList::SubDContentSerialNumber() const
+{
+ return m_subd_content_serial_number;
+}
+
+unsigned int ON_SubDComponentList::Count() const
+{
+ return m_component_list.UnsignedCount();
+}
+
+const ON_SubDComponentPtr ON_SubDComponentList::operator[](int i) const
+{
+ return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentList::operator[](unsigned int i) const
+{
+ return i < m_component_list.UnsignedCount() ? m_component_list[i] : ON_SubDComponentPtr::Null;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__INT64 i) const
+{
+ return i >= 0 && i < ((ON__INT64)m_component_list.Count()) ? m_component_list[i] : ON_SubDComponentPtr::Null;
+}
+
+const ON_SubDComponentPtr ON_SubDComponentList::operator[](ON__UINT64 i) const
+{
+ return i < ((ON__UINT64)m_component_list.UnsignedCount()) ? m_component_list[i] : ON_SubDComponentPtr::Null;
+}
+
+#if defined(ON_RUNTIME_APPLE)
+const ON_SubDComponentPtr ON_SubDComponentList::operator[](size_t i) const
+{
+ return i >= 0 && i < m_component_list.Count() ? m_component_list[i] : ON_SubDComponentPtr::Null;
+}
+#endif
+
+const ON_SimpleArray< ON_SubDComponentPtr >& ON_SubDComponentList::ComponentList() const
+{
+ return this->m_component_list;
+}
+
+const ON_SubD& ON_SubDComponentList::SubD() const
+{
+ return m_subd;
+}
+
+ON__UINT64 ON_SubDComponentList::UpdateContentSerialNumber()
+{
+ m_subd_content_serial_number = m_subd.ContentSerialNumber();
+ return m_subd_content_serial_number;
+}
+
+unsigned int ON_SubDComponentList::UpdateSubDForExperts(const ON_SubD & subd, bool bUpdateDeletedComponents)
+{
+ const unsigned count0 = Count();
+ if (subd.RuntimeSerialNumber() == m_subd.RuntimeSerialNumber())
+ return count0; // the components in this list are in subd.
+
+ // Use the component ids to update the list to reference components in subd.
+ unsigned count1 = 0;
+ for (unsigned i = 0; i < count0; ++i)
+ {
+ ON_SubDComponentPtr cptr0 = m_component_list[i];
+ const ON_SubDComponentBase* c0 = cptr0.ComponentBase();
+ if (nullptr == c0)
+ continue;
+ if (false == bUpdateDeletedComponents && false == c0->IsActive())
+ continue;
+ ON_COMPONENT_INDEX ci = cptr0.ComponentIndex();
+ if (0 == ci.m_index)
+ continue;
+ ON_SubDComponentPtr cptr1 = subd.ComponentPtrFromComponentIndex(ci);
+ if (cptr1.IsNull())
+ continue;
+ if (0 != cptr0.ComponentDirection())
+ cptr1.SetComponentDirection();
+ m_component_list[count1++] = cptr1;
+ }
+ m_component_list.SetCount(count1);
+ m_subd.ShareDimple(subd);
+ m_subd_runtime_serial_number = m_subd.RuntimeSerialNumber();
+ m_subd_content_serial_number = m_subd.ContentSerialNumber();
+ return Count();
+}
+
+unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list)
+{
+ ON_SubDComponentMarksClearAndRestore saved_marks(subd);
+ const unsigned count = component_list.UnsignedCount();
+ unsigned marked_count = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_COMPONENT_INDEX ci = component_list[i];
+ if (ON_COMPONENT_INDEX::TYPE::subd_vertex != ci.m_type)
+ continue;
+ const unsigned vertex_id = (unsigned)ci.m_index;
+ const ON_SubDVertex* v = subd.VertexFromId(vertex_id);
+ if (nullptr == v)
+ continue;
+ if (v->m_status.RuntimeMark())
+ continue;
+ v->m_status.SetRuntimeMark();
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, true, true, true, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list)
+{
+ ON_SubDComponentMarksClearAndRestore saved_marks(subd);
+ const unsigned count = component_list.UnsignedCount();
+ unsigned marked_count = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_SubDComponentBase* c = component_list[i].ComponentBase();
+ if (nullptr == c)
+ continue;
+ if (c->m_status.RuntimeMark())
+ continue;
+ c->m_status.SetRuntimeMark();
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, true, true, true, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromVertexIdList(const ON_SubD& subd, const ON_SimpleArray& vertex_id_list)
+{
+ ON_SubDComponentMarksClearAndRestore saved_marks(subd);
+ const unsigned count = vertex_id_list.UnsignedCount();
+ unsigned marked_count = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const unsigned vertex_id = vertex_id_list[i];
+ if (vertex_id <= 0 || vertex_id >= ON_UNSET_UINT_INDEX)
+ continue;
+ const ON_SubDVertex* v = subd.VertexFromId(vertex_id);
+ if (nullptr == v)
+ continue;
+ if (v->m_status.RuntimeMark())
+ continue;
+ v->m_status.SetRuntimeMark();
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, false, false, true, marked_count);
+}
+
+
+unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list)
+{
+ ON_SubDComponentMarksClearAndRestore saved_marks(subd);
+ const unsigned count = vertex_list.UnsignedCount();
+ unsigned marked_count = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_SubDVertex* v = vertex_list[i].Vertex();
+ if (nullptr == v)
+ continue;
+ if (v->m_status.RuntimeMark())
+ continue;
+ v->m_status.SetRuntimeMark();
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, false, false, true, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& vertex_list)
+{
+ ON_SubDComponentMarksClearAndRestore saved_marks(subd);
+ const unsigned count = vertex_list.UnsignedCount();
+ unsigned marked_count = 0;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ON_SubDVertex* v = vertex_list[i];
+ if (nullptr == v)
+ continue;
+ if (v->m_status.RuntimeMark())
+ continue;
+ v->m_status.SetRuntimeMark();
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, false, false, true, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromMarkedComponents(const ON_SubD& subd, bool bComponentInListMark)
+{
+ unsigned marked_count = 0;
+ ON_SubDComponentIterator cit(subd);
+ if (bComponentInListMark)
+ bComponentInListMark = true; // avoid other byte values.
+ for (ON_SubDComponentPtr c = cit.FirstComponent(); c.IsNotNull(); c = cit.NextComponent())
+ {
+ if (bComponentInListMark != c.Mark())
+ continue;
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, true, true, bComponentInListMark, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromMarkedVertices(const ON_SubD& subd, bool bVertexInListMark)
+{
+ unsigned marked_count = 0;
+ ON_SubDVertexIterator vit(subd);
+ if (bVertexInListMark)
+ bVertexInListMark = true; // avoid other byte values.
+ for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
+ {
+ if (bVertexInListMark != v->m_status.RuntimeMark())
+ continue;
+ ++marked_count;
+ }
+ return Internal_Create(subd, true, false, false, bVertexInListMark, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromMarkedEdges(const ON_SubD& subd, bool bEdgeInListMark)
+{
+ unsigned marked_count = 0;
+ ON_SubDEdgeIterator eit(subd);
+ if (bEdgeInListMark)
+ bEdgeInListMark = true; // avoid other byte values.
+ for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
+ {
+ if (bEdgeInListMark != e->m_status.RuntimeMark())
+ continue;
+ ++marked_count;
+ }
+ return Internal_Create(subd, false, true, false, bEdgeInListMark, marked_count);
+}
+
+unsigned ON_SubDComponentList::CreateFromMarkedFaces(const ON_SubD& subd, bool bFaceInListMark)
+{
+ unsigned marked_count = 0;
+ ON_SubDFaceIterator fit(subd);
+ if (bFaceInListMark)
+ bFaceInListMark = true; // avoid other byte values.
+ for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
+ {
+ if (bFaceInListMark != f->m_status.RuntimeMark())
+ continue;
+ ++marked_count;
+ }
+ return Internal_Create(subd, false, false, true, bFaceInListMark, marked_count);
+}
+
+unsigned ON_SubDComponentList::Internal_Create(const ON_SubD & subd, bool bAddVertices, bool bAddEdges, bool bAddFaces, bool bComponentInListMark, unsigned marked_component_count)
+{
+ Destroy();
+
+ if (0 == marked_component_count)
+ return 0;
+
+ const unsigned face_count = bAddFaces ? subd.FaceCount() : 0U;
+ const unsigned edge_count = bAddEdges ? subd.EdgeCount() : 0U;
+ const unsigned vertex_count = bAddVertices ? subd.VertexCount() : 0U;
+ if (0 == vertex_count && 0 == edge_count && 0 == face_count)
+ return 0;
+
+ if (marked_component_count > vertex_count + edge_count + face_count)
+ return 0;
+
+ bComponentInListMark = bComponentInListMark ? true : false;
+ m_component_list.Reserve(marked_component_count);
+ m_component_list.SetCount(0);
+ if (vertex_count > 0)
+ {
+ ON_SubDVertexIterator vit(subd);
+ for (const ON_SubDVertex* v = vit.FirstVertex(); nullptr != v; v = vit.NextVertex())
+ {
+ if (bComponentInListMark != v->m_status.RuntimeMark())
+ continue;
+ m_component_list.Append(v->ComponentPtr());
+ }
+ }
+ if (edge_count > 0)
+ {
+ ON_SubDEdgeIterator eit(subd);
+ for (const ON_SubDEdge* e = eit.FirstEdge(); nullptr != e; e = eit.NextEdge())
+ {
+ if (bComponentInListMark != e->m_status.RuntimeMark())
+ continue;
+ m_component_list.Append(e->ComponentPtr());
+ }
+ }
+ if (face_count > 0)
+ {
+ ON_SubDFaceIterator fit(subd);
+ for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
+ {
+ if (bComponentInListMark != f->m_status.RuntimeMark())
+ continue;
+ m_component_list.Append(f->ComponentPtr());
+ }
+ }
+
+ if (m_component_list.UnsignedCount() > 0)
+ {
+ m_subd.ShareDimple(subd);
+ m_subd_runtime_serial_number = subd.RuntimeSerialNumber();
+ m_subd_content_serial_number = subd.ContentSerialNumber();
+ }
+ return m_component_list.UnsignedCount();
+}
+
+unsigned int ON_SubDComponentList::RemoveAllComponents()
+{
+ const unsigned count0 = Count();
+ m_component_list.SetCount(0);
+ return count0;
+}
+
+unsigned int ON_SubDComponentList::RemoveAllVertices()
+{
+ return Internal_RemoveComponents(true, false, false);
+}
+
+unsigned int ON_SubDComponentList::RemoveAllEdges()
+{
+ return Internal_RemoveComponents(false, true, false);
+}
+
+unsigned int ON_SubDComponentList::RemoveAllFaces()
+{
+ return Internal_RemoveComponents(false, false, true);
+}
+
+unsigned ON_SubDComponentList::Internal_RemoveComponents(
+ bool bRemoveVertices,
+ bool bRemoveEdges,
+ bool bRemoveFaces
+)
+{
+ unsigned int count0 = Count();
+ if (bRemoveVertices || bRemoveEdges || bRemoveFaces)
+ {
+ unsigned count1 = 0;
+ for (unsigned i = 0; i < count0; ++i)
+ {
+ const ON_SubDComponentPtr cptr = m_component_list[i];
+ bool bRemove = false;
+ switch (cptr.ComponentType())
+ {
+ case ON_SubDComponentPtr::Type::Vertex:
+ bRemove = bRemoveVertices;
+ break;
+ case ON_SubDComponentPtr::Type::Edge:
+ bRemove = bRemoveEdges;
+ break;
+ case ON_SubDComponentPtr::Type::Face:
+ bRemove = bRemoveFaces;
+ break;
+ default:
+ bRemove = true;
+ }
+ if (bRemove)
+ continue;
+ m_component_list[count1++] = cptr;
+ }
+ m_component_list.SetCount(count1);
+ }
+ return count0 - Count();
+}
+
+
+void ON_SubDComponentList::Destroy()
+{
+ m_subd_runtime_serial_number = 0;
+ m_subd_content_serial_number = 0;
+ m_component_list.SetCount(0);
+ m_subd.ShareDimple(ON_SubD::Empty);
+}
+
+const ON_SubDComponentFilter ON_SubDComponentFilter::Create(
+ bool bAcceptVertices,
+ bool bAcceptEdges,
+ bool bAcceptFaces
+)
+{
+ ON_SubDComponentFilter f;
+ if (false == bAcceptVertices)
+ f.m_bRejectVertices = true;
+ if (false == bAcceptEdges)
+ f.m_bRejectEdges = true;
+ if (false == bAcceptFaces)
+ f.m_bRejectFaces = true;
+ return f;
+}
+
+bool ON_SubDComponentFilter::AcceptComponent(ON_COMPONENT_INDEX component_index, const class ON_Geometry* geometry) const
+{
+ if (false == component_index.IsSubDComponentIndex())
+ return false;
+ const ON_SubDComponentRef* cref = ON_SubDComponentRef::Cast(geometry);
+ if (nullptr == cref)
+ return false;
+ const ON_SubDComponentPtr cptr = cref->ComponentPtr();
+ if (component_index.m_index != (int)cptr.ComponentId())
+ return false;
+ switch (component_index.m_type)
+ {
+ case ON_COMPONENT_INDEX::TYPE::subd_vertex:
+ if (ON_SubDComponentPtr::Type::Vertex != cptr.ComponentType())
+ return false;
+ break;
+ case ON_COMPONENT_INDEX::TYPE::subd_edge:
+ if (ON_SubDComponentPtr::Type::Edge != cptr.ComponentType())
+ return false;
+ break;
+ case ON_COMPONENT_INDEX::TYPE::subd_face:
+ if (ON_SubDComponentPtr::Type::Face != cptr.ComponentType())
+ return false;
+ break;
+ }
+ return AcceptComponent(cptr);
+}
+
+bool ON_SubDComponentFilter::AcceptComponent(const class ON_Geometry* geometry) const
+{
+ return AcceptComponent(ON_SubDComponentRef::Cast(geometry));
+}
+
+
+bool ON_SubDComponentFilter::AcceptComponent(const class ON_SubDComponentRef* cref) const
+{
+ return (nullptr != cref) ? AcceptComponent(cref->ComponentPtr()) : false;
+}
+
+bool ON_SubDComponentFilter::AcceptComponent(ON_SubDComponentPtr cptr) const
+{
+ switch (cptr.ComponentType())
+ {
+ case ON_SubDComponentPtr::Type::Vertex:
+ return AcceptVertex(cptr.Vertex());
+ break;
+ case ON_SubDComponentPtr::Type::Edge:
+ return AcceptEdge(cptr.Edge());
+ break;
+ case ON_SubDComponentPtr::Type::Face:
+ return AcceptFace(cptr.Face());
+ break;
+ }
+ return false;
+}
+
+bool ON_SubDComponentFilter::AcceptVertex(ON_SubDVertexPtr vptr) const
+{
+ return AcceptVertex(vptr.Vertex());
+}
+
+bool ON_SubDComponentFilter::AcceptEdge(ON_SubDEdgePtr eptr) const
+{
+ return AcceptEdge(eptr.Edge());
+}
+
+
+bool ON_SubDComponentFilter::AcceptFace(ON_SubDFacePtr fptr) const
+{
+ return AcceptFace(fptr.Face());
+}
+
+bool ON_SubDComponentFilter::AcceptVertex(const ON_SubDVertex * v) const
+{
+ if (m_bRejectVertices)
+ return false;
+
+ if (nullptr == v)
+ return false;
+
+ if (false == AcceptVertexTag(v->m_vertex_tag))
+ return false;
+
+ if (ON_SubDComponentFilter::Topology::Unset != m_vertex_topology_filter)
+ {
+ // check boundary/interior/nonmanifold
+ if (v->HasBoundaryVertexTopology())
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_vertex_topology_filter)))
+ return false;
+ }
+ else if (v->HasInteriorVertexTopology())
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_vertex_topology_filter)))
+ return false;
+ }
+ else
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_vertex_topology_filter)))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ON_SubDComponentFilter::AcceptEdge(const ON_SubDEdge * e) const
+{
+ if (m_bRejectEdges)
+ return false;
+
+ if (nullptr == e)
+ return false;
+
+ if (false == AcceptEdgeTag(e->m_edge_tag))
+ return false;
+
+ if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter)
+ {
+ // check boundary/interior/nonmanifold
+ if (1 == e->m_face_count)
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ else if (2 == e->m_face_count)
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ else
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ON_SubDComponentFilter::AcceptFace(const ON_SubDFace * f) const
+{
+ if (m_bRejectFaces)
+ return false;
+
+ if (nullptr == f)
+ return false;
+
+ if (m_maximum_face_edge_count > 0U)
+ {
+ const unsigned face_edge_count = f->m_edge_count;
+ if (face_edge_count < m_minimum_face_edge_count || face_edge_count > m_maximum_face_edge_count)
+ return false;
+ }
+
+ if (ON_SubDComponentFilter::Topology::Unset != m_edge_topology_filter)
+ {
+ const ON_SubDEdgePtr* eptr = f->m_edge4;
+ for (unsigned short fei = 0; fei < f->m_edge_count; ++fei, ++eptr)
+ {
+ if (4 == fei)
+ {
+ eptr = f->m_edgex;
+ if (nullptr == eptr)
+ break;
+ }
+ const ON_SubDEdge* e = eptr->Edge();
+ if (nullptr == e)
+ continue;
+ if (1 == e->m_face_count)
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Boundary)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ else if (2 == e->m_face_count)
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Interior)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ else
+ {
+ if (0 == (static_cast(ON_SubDComponentFilter::Topology::Nonmanifold)& static_cast(m_edge_topology_filter)))
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void ON_SubDComponentFilter::SetAcceptVertices(bool bAcceptVertices)
+{
+ m_bRejectVertices = bAcceptVertices ? false : true;
+}
+
+bool ON_SubDComponentFilter::AcceptVertices() const
+{
+ return false == m_bRejectVertices;
+}
+
+void ON_SubDComponentFilter::SetAcceptEdges(bool bAcceptEdges)
+{
+ m_bRejectEdges = bAcceptEdges ? false : true;
+}
+
+bool ON_SubDComponentFilter::AcceptEdges() const
+{
+ return false == m_bRejectEdges;
+}
+
+void ON_SubDComponentFilter::SetAcceptFaces(bool bAcceptFaces)
+{
+ m_bRejectFaces = bAcceptFaces ? false : true;
+}
+
+bool ON_SubDComponentFilter::AcceptFaces() const
+{
+ return false == m_bRejectFaces;
+}
+
+void ON_SubDComponentFilter::SetVertexTopologyFilter(ON_SubDComponentFilter::Topology vertex_topology_filter)
+{
+ m_vertex_topology_filter = vertex_topology_filter;
+}
+
+void ON_SubDComponentFilter::ClearVertexTopologyFilter()
+{
+ m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+}
+
+ON_SubDComponentFilter::Topology ON_SubDComponentFilter::VertexTopologyFilter() const
+{
+ return m_vertex_topology_filter;
+}
+
+void ON_SubDComponentFilter::SetEdgeTopologyFilter(ON_SubDComponentFilter::Topology edge_topology_filter)
+{
+ m_edge_topology_filter = edge_topology_filter;
+}
+
+ON_SubDComponentFilter::Topology ON_SubDComponentFilter::EdgeTopologyFilter() const
+{
+ return m_edge_topology_filter;
+}
+
+void ON_SubDComponentFilter::ClearEdgeTopologyFilter()
+{
+ m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+}
+
+void ON_SubDComponentFilter::SetFaceTopologyFilter(ON_SubDComponentFilter::Topology face_topology_filter)
+{
+ m_face_topology_filter = face_topology_filter;
+}
+
+ON_SubDComponentFilter::Topology ON_SubDComponentFilter::FaceTopologyFilter() const
+{
+ return m_face_topology_filter;
+}
+
+void ON_SubDComponentFilter::ClearFaceTopologyFilter()
+{
+ m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+}
+
+bool ON_SubDComponentFilter::AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const
+{
+ if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[0])
+ return true; // no tag filter
+
+ for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i)
+ {
+ if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i])
+ break;
+ if (m_vertex_tag_filter[i] != vertex_tag)
+ continue;
+ return true;
+ }
+ return false;
+}
+
+void ON_SubDComponentFilter::AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag)
+{
+ for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i)
+ {
+ if (vertex_tag == m_vertex_tag_filter[i])
+ break;
+ if (ON_SubD::VertexTag::Unset == m_vertex_tag_filter[i])
+ {
+ m_vertex_tag_filter[i] = vertex_tag;
+ break;
+ }
+ }
+}
+
+void ON_SubDComponentFilter::ClearVertexTagFilter()
+{
+ for (size_t i = 0; i < sizeof(m_vertex_tag_filter) / sizeof(m_vertex_tag_filter[0]); ++i)
+ m_vertex_tag_filter[i] = ON_SubD::VertexTag::Unset;
+}
+
+
+bool ON_SubDComponentFilter::AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const
+{
+ if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[0])
+ return true; // no tag filter
+
+ for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i)
+ {
+ if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i])
+ break;
+ if (m_edge_tag_filter[i] != edge_tag)
+ continue;
+ return true;
+ }
+ return false;
+}
+
+void ON_SubDComponentFilter::AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag)
+{
+ for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i)
+ {
+ if (edge_tag == m_edge_tag_filter[i])
+ break;
+ if (ON_SubD::EdgeTag::Unset == m_edge_tag_filter[i])
+ {
+ m_edge_tag_filter[i] = edge_tag;
+ break;
+ }
+ }
+}
+
+void ON_SubDComponentFilter::ClearEdgeTagFilter()
+{
+ for (size_t i = 0; i < sizeof(m_edge_tag_filter) / sizeof(m_edge_tag_filter[0]); ++i)
+ m_edge_tag_filter[i] = ON_SubD::EdgeTag::Unset;
+}
+
+bool ON_SubDComponentFilter::AcceptFaceEdgeCount(
+ unsigned face_edge_count
+) const
+{
+ return (m_maximum_face_edge_count >= 3U) ? (face_edge_count >= m_minimum_face_edge_count && face_edge_count <= m_maximum_face_edge_count) : false;
+}
+
+void ON_SubDComponentFilter::SetFaceEdgeCountFilter(
+ unsigned minimum_face_edge_count,
+ unsigned maximum_face_edge_count
+)
+{
+ if (minimum_face_edge_count <= maximum_face_edge_count && maximum_face_edge_count >= 3U)
+ {
+ m_minimum_face_edge_count = minimum_face_edge_count;
+ m_maximum_face_edge_count = maximum_face_edge_count;
+ }
+}
+
+void ON_SubDComponentFilter::ClearFaceEdgeCountFilter()
+{
+ m_minimum_face_edge_count = 0U;
+ m_maximum_face_edge_count = 0U;
+}
+
+
#if defined(ON_SUBD_CENSUS)
//////////////////////////////////////////////////////////////////////////
diff --git a/opennurbs_subd.h b/opennurbs_subd.h
index a0470ff4..942eb420 100644
--- a/opennurbs_subd.h
+++ b/opennurbs_subd.h
@@ -374,6 +374,40 @@ public:
int relative_vertex_index
) const;
+ /*
+ Parameters:
+ relative_vertex_index - [in]
+ 0: return Edge()->Vertex(EdgeDirection())
+ 1: return Edge()->Vertex(1-EdgeDirection())
+ Returns:
+ The requested vertex control net point EdgeDirection() taken into account.
+ ON_3dPoint::NanPoint if relative_vertex_index, Edge() is nullptr, or Edge()->Vertex() is nullptr.
+ */
+ const ON_3dPoint RelativeControlNetPoint(
+ int relative_vertex_index
+ ) const;
+
+ const ON_Line RelativeControlNetLine() const;
+
+ const ON_3dVector RelativeControlNetDirection() const;
+
+ /*
+ Parameters:
+ relative_vertex_index - [in]
+ Returns:
+ If Edge() not nullptr, then
+ If (relative_vertex_index = 0), returns Edge()->m_sector_coefficient(EdgeDirection())
+ If (relative_vertex_index = 0), returns Edge()->m_sector_coefficient(1-EdgeDirection())
+ Otherwise ON_SubDSectorType::ErrorSectorCoefficient is returned.
+ Remarks:
+ The name "sector coefficient" is used because is is a property of the
+ vertex's sector (every edge in vertex sector has the same value at the tagged vertex).
+ The sector coefficient does not change which a subdivision is applied.
+ */
+ double RelativeSectorCoefficient(
+ int relative_vertex_index
+ ) const;
+
/*
Returns:
The vector from RelativeVertex(0)->ControlNetPoint() to RelativeVertex(1)->ControlNetPoint(),
@@ -422,6 +456,33 @@ public:
const class ON_SubDComponentPtr& edge_component
);
+ /*
+ Parameters:
+ edge - [in]
+ start_vertex - [in]
+ One of the edge's vertices.
+ Returns:
+ An ON_SubDEdgePtr pointing at edge with RelativeVertex(0) = start_vertex.
+ */
+ static const ON_SubDEdgePtr CreateFromStartVertex(
+ const class ON_SubDEdge* edge,
+ const ON_SubDVertex* start_vertex
+ );
+
+ /*
+ Parameters:
+ edge - [in]
+ end_vertex - [in]
+ One of the edge's vertices.
+ Returns:
+ An ON_SubDEdgePtr pointing at edge with RelativeVertex(1) = end_vertex.
+ */
+ static const ON_SubDEdgePtr CreateFromEndVertex(
+ const class ON_SubDEdge* edge,
+ const ON_SubDVertex* end_vertex
+ );
+
+
/*
Returns:
The current value of the component mark ( m_status->RuntimeMark() ).
@@ -526,6 +587,12 @@ public:
const ON_ComponentStatus Status() const;
+ /*
+ Returns:
+ A ON_SubDFacePtr pointing at the same face with the direction reversed from this.
+ */
+ const ON_SubDFacePtr Reversed() const;
+
static const ON_SubDFacePtr Create(
const class ON_SubDFace* face,
ON__UINT_PTR direction
@@ -964,6 +1031,21 @@ public:
*/
ON_SubDComponentPtr::Type ComponentType() const;
+ /*
+ Returns true if First() is ON_SubDComponentPtr::Null.
+ */
+ bool FirstIsNull() const;
+
+ /*
+ Returns true if Second() is ON_SubDComponentPtr::Null.
+ */
+ bool SecondIsNull() const;
+
+ /*
+ Returns true if both First() and Second() are ON_SubDComponentPtr::Null.
+ */
+ bool BothAreNull() const;
+
public:
const static ON_SubDComponentPtrPair Null;
@@ -1541,9 +1623,6 @@ public:
class ON_NurbsSurface* m_nurbs_surface = nullptr;
};
-
-
-
//////////////////////////////////////////////////////////////////////////
//
// ON_SubD
@@ -1853,7 +1932,24 @@ public:
/// Every edge in an edge chain has the same smooth/crease edge tag
/// and interior vertices have the corresponding smooth/crease vertex tag.
///
- EqualEdgeAndVertexTag = 3
+ EqualEdgeAndVertexTag = 3,
+
+ ///
+ /// Every edge in an edge chain has the same smooth/crease property
+ /// and every edge has the same number of faces.
+ /// If the edges have 1 face, then interior vertices have valence = 3.
+ /// If the edges have 2 faces, then interior vertices have valence = 4.
+ ///
+ EqualEdgeTagAndOrdinary = 4,
+
+ ///
+ /// Every edge in an edge chain has the same smooth/crease edge tag,
+ /// every edge has the same number of faces,
+ /// and interior vertices have the corresponding smooth/crease vertex tag.
+ /// If the edges have 1 face, then interior vertices have valence = 3.
+ /// If the edges have 2 faces, then interior vertices have valence = 4.
+ ///
+ EqualEdgeAndVertexTagAndOrdinary = 5
};
#pragma endregion
@@ -2610,7 +2706,11 @@ public:
*/
const class ON_SubDVertex* VertexFromId(
unsigned int vertex_id
- ) const;
+ ) const;
+
+ const class ON_SubDVertex* VertexFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+ ) const;
/////////////////////////////////////////////////////////
//
@@ -2651,6 +2751,10 @@ public:
unsigned int edge_id
) const;
+ const class ON_SubDEdge* EdgeFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+ ) const;
+
/////////////////////////////////////////////////////////
//
// Face access
@@ -2690,6 +2794,12 @@ public:
unsigned int face_id
) const;
+
+ const class ON_SubDFace* FaceFromComponentIndex(
+ ON_COMPONENT_INDEX component_index
+ ) const;
+
+
/////////////////////////////////////////////////////////
//
// Component (vertex, edge, face) state ( selected, highlighted, ... ) tools
@@ -2910,12 +3020,17 @@ public:
Returns:
Merged edge (eptr0) or ON_SubDEdgePtr::Null if edges could not be merged
*/
- ON_SubDEdgePtr MergeEdges(
+ ON_SubDEdgePtr MergeConsecutiveEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
);
- static bool EdgesCanBeMerged(
+ /*
+ Returns:
+ True if eptr0.RelativeVetex(1) == eptr1.RelativeVetex(0) and both edges
+ have the same set of faces.
+ */
+ static bool EdgesAreConsecutive(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
);
@@ -3099,7 +3214,7 @@ public:
sector weights will be checked and updated as needed.
Returns:
Number of edges that had a tag value changed or sector
- coefficient set to ON_SubDSectorType::UnsetSectorWeight.
+ coefficient set to ON_SubDSectorType::UnsetSectorCoefficient.
Remarks:
It is easiest to call UpdateAllTagsAndSectorCoefficients().
*/
@@ -3123,7 +3238,7 @@ public:
sector weights will be checked and updated as needed.
Returns:
Number of edges that had a tag value changed or sector
- coefficient set to ON_SubDSectorType::UnsetSectorWeight.
+ coefficient set to ON_SubDSectorType::UnsetSectorCoefficient.
Remarks:
It is easiest to call UpdateAllTagsAndSectorCoefficients().
*/
@@ -3379,6 +3494,7 @@ public:
ON_SubD::EdgeTag edge_tag
);
+
/*
Description:
Remove all interior creases.
@@ -4054,6 +4170,19 @@ public:
void ClearEvaluationCache() const;
+ /*
+ Description:
+ Clear all cached evaluation information (meshes, surface points, boundiang boxes, ...)
+ that depends on the vertex's control point location or tag.
+ Parameter:
+ vertex - [in]
+ */
+ void ClearNeighborhoodEvaluationCache(
+ const ON_SubDVertex* vertex,
+ bool bTagChanged
+ ) const;
+
+
/*
Description:
Get a mesh of the subdivision control net.
@@ -4072,6 +4201,9 @@ public:
+
+
+
#pragma region RH_C_SHARED_ENUM [ON_SubD::NurbsSurfaceType] [Rhino.Geometry.SubD.NurbsSurfaceType] [nested:byte]
///
/// ON_SubD::NurbsSurfaceType specifies what type of NURBS surfaces are returned by ON_SubD.GetSurfaceNurbsFragments()
@@ -4488,6 +4620,143 @@ private:
#pragma ON_PRAGMA_WARNING_POP
};
+//////////////////////////////////////////////////////////////////////////
+//
+// ON_SubDComponentList
+//
+class ON_CLASS ON_SubDComponentList
+{
+public:
+ ON_SubDComponentList() = default;
+ ~ON_SubDComponentList() = default;
+ ON_SubDComponentList(const ON_SubDComponentList&);
+ ON_SubDComponentList& operator=(const ON_SubDComponentList&);
+
+public:
+ static const ON_SubDComponentList Empty;
+
+public:
+ unsigned CreateFromMarkedComponents(const ON_SubD& subd, bool bComponentInListMark);
+ unsigned CreateFromMarkedVertices(const ON_SubD& subd, bool bVertexInListMark);
+ unsigned CreateFromMarkedEdges(const ON_SubD& subd, bool bEdgeInListMark);
+ unsigned CreateFromMarkedFaces(const ON_SubD& subd, bool bFaceInListMark);
+
+ unsigned CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list);
+ unsigned CreateFromComponentList(const ON_SubD& subd, const ON_SimpleArray& component_list);
+
+ unsigned CreateFromVertexIdList(const ON_SubD& subd, const ON_SimpleArray& free_vertex_ids);
+ unsigned CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& free_vertices);
+ unsigned CreateFromVertexList(const ON_SubD& subd, const ON_SimpleArray& free_vertices);
+
+ void Destroy();
+
+ /*
+ Returns:
+ Number of removed components.
+ */
+ unsigned int RemoveAllComponents();
+
+ /*
+ Returns:
+ Number of removed components.
+ */
+ unsigned int RemoveAllVertices();
+
+ /*
+ Returns:
+ Number of removed components.
+ */
+ unsigned int RemoveAllEdges();
+
+ /*
+ Returns:
+ Number of removed components.
+ */
+ unsigned int RemoveAllFaces();
+
+ /*
+ Returns:
+ SubD runtime serial number.
+ */
+ ON__UINT64 SubDRuntimeSerialNumber() const;
+
+ /*
+ Returns:
+ SubD content serial number when this list was created or the last
+ time UpdateContentSerialNumber() was run.
+ */
+ ON__UINT64 SubDContentSerialNumber() const;
+
+ unsigned int Count() const;
+
+ /*
+ operator[] returns ON_SubDComponentPtr::Null when index is out of bounds.
+ */
+ const ON_SubDComponentPtr operator[](int) const;
+ const ON_SubDComponentPtr operator[](unsigned int) const;
+ const ON_SubDComponentPtr operator[](ON__INT64) const;
+ const ON_SubDComponentPtr operator[](ON__UINT64) const;
+#if defined(ON_RUNTIME_APPLE)
+ const ON_SubDComponentPtr operator[](size_t) const;
+#endif
+
+ const ON_SimpleArray< ON_SubDComponentPtr >& ComponentList() const;
+
+ const ON_SubD& SubD() const;
+
+ /*
+ Description:
+ Update the saved subd content serial number to the current value of SubD().ContentSerialNumber().
+ Returns:
+ Updated value of subd content serial number.
+ */
+ ON__UINT64 UpdateContentSerialNumber();
+
+ /*
+ Description:
+ Change the component list to reference components in a different subd.
+ Parameters:
+ new_subd - [in]
+ subd to replace current referenced subd
+ bUpdateDeletedComponents - [in]
+ false: current components that are deleted will be ignored.
+ true: if the corresponding component in new_sub is not deleted, it
+ will be added to the list.
+ Returns:
+ Number of components in list after updating.
+ */
+ unsigned int UpdateSubDForExperts(const ON_SubD& subd, bool bUpdateDeletedComponents);
+
+
+private:
+ unsigned Internal_Create(
+ const ON_SubD& subd,
+ bool bAddVertices,
+ bool bAddEdges,
+ bool bAddFaces,
+ bool bComponentInListMark,
+ unsigned marked_component_count
+ );
+
+ unsigned Internal_RemoveComponents(
+ bool bRemoveVertices,
+ bool bRemoveEdges,
+ bool bRemoveFaces
+ );
+
+private:
+ ON__UINT64 m_subd_runtime_serial_number = 0;
+ ON__UINT64 m_subd_content_serial_number = 0;
+
+ unsigned m_subd_vertex_count = 0;
+ unsigned m_subd_edge_count = 0;
+ unsigned m_subd_face_count = 0;
+ unsigned m_reserved = 0;
+
+private:
+ ON_SubD m_subd; // keeps subd dimple in scope while m_component_list[] is active
+ ON_SimpleArray< ON_SubDComponentPtr > m_component_list;
+};
//////////////////////////////////////////////////////////////////////////
//
@@ -4496,9 +4765,9 @@ private:
class ON_SubDComponentMarksClearAndRestore
{
public:
- // Constructor saves current component RuntimeMark() settings.
+ // Constructor saves current component RuntimeMark() settings and then clears them.
ON_SubDComponentMarksClearAndRestore(
- ON_SubD& subd
+ const ON_SubD& subd
);
// Destructor restores saved marks.
@@ -4559,7 +4828,7 @@ public:
/////////////////////////////////////////////////////////////////////////////////////
//
- // Sector Weights
+ // Sector Coefficients
//
/////////////////////////////////////////////////////////////////////////////////////
//
@@ -4644,10 +4913,10 @@ public:
Returns:
w: 0.0 <= w < 1.0
w = sector theta value.
- ON_SubDSectorType::ErrorSectorWeight
+ ON_SubDSectorType::ErrorSectorCoefficient
This ON_SubDSectorType is not valid and the calculation failed.
*/
- double SectorWeight() const;
+ double SectorCoefficient() const;
unsigned int FacetEdgeCount() const;
@@ -4839,9 +5108,9 @@ public:
public:
/*
Returns:
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
*/
- static double SmoothSectorWeight();
+ static double SmoothSectorCoefficient();
/*
Parameters:
@@ -4851,7 +5120,7 @@ public:
Returns:
0:
failed to caclulate weight
- ON_SubDSectorType::UnsetSectorWeight:
+ ON_SubDSectorType::UnsetSectorCoefficient:
This typically happens when a SubD control net is being
created and a facet type is not specified.
The weights will be calculated at the first subdivision.
@@ -4862,15 +5131,15 @@ public:
This is a useful tool when calling AddEdge while a subdivision
level is being constructed.
*/
- static double CreaseSectorWeight(
+ static double CreaseSectorCoefficient(
unsigned int sector_face_count
);
- static double DartSectorWeight(
+ static double DartSectorCoefficient(
unsigned int sector_face_count
);
- static double CornerSectorWeight(
+ static double CornerSectorCoefficient(
unsigned int sector_face_count,
double corner_sector_angle_radians
);
@@ -4901,33 +5170,33 @@ public:
static const double ErrorSectorTheta; // = -9992.0;
- // This value is is used to set sector weights when the
+ // This value is is used to set edge sector coefficients when the
// actual value is not needed. This occurs at both ends
// of a creased edge and when the end of a smooth edge
// is a smooth vertex.
- static const double IgnoredSectorWeight; // = 0.0;
+ static const double IgnoredSectorCoefficient; // = 0.0;
- // This value is used to mark sector weights that need to be
+ // This value is used to mark edge sector coefficients that need to be
// set in the future when more information is available.
// It is typically used when creating a subD control net
// and the facet type is not known. Any value < 0.0 and not
// equal to ON_UNSET_VALUE would work. The fact that the actual
// value is -999.0 has no other significance.
- static const double UnsetSectorWeight; // = -8883.0;
+ static const double UnsetSectorCoefficient; // = -8883.0;
- // This value is indicate a sector weight calculation failed.
- static const double ErrorSectorWeight; // = -9993.0;
+ // This value indicates an edge sector coefficient calculation failed.
+ static const double ErrorSectorCoefficient; // = -9993.0;
- static bool IsValidSectorWeightValue(
+ static bool IsValidSectorCoefficientValue(
double weight_value,
- bool bAllowUnsetTaggedEndWeight
+ bool bAllowUnsetTaggedEndCoefficient
);
/*
Returns:
- ON_SubDSectorType::ErrorSectorWeight and calls debug breakpoint
+ ON_SubDSectorType::ErrorSectorCoefficient and calls debug breakpoint
*/
- static double SectorWeightCalculationError();
+ static double SectorCoefficientCalculationError();
/*
@@ -5349,7 +5618,7 @@ private:
unsigned int m_hash = 0; // SetHash() sets this field, SectorTypeHash() returns its value.
unsigned int m_corner_sector_angle_index = 0; // >= 0 and <= ON_SubDSectorType::MaximumCornerAngleIndex
unsigned int m_sector_face_count = 0;
- double m_sector_weight = 0.0;
+ double m_sector_coefficient = 0.0;
double m_sector_theta = 0.0;
double m_corner_sector_angle_radians = 0.0;
@@ -5426,7 +5695,7 @@ private:
Returns:
0:
failed to caclulate weight
- ON_SubDSectorType::ErrorSectorWeight:
+ ON_SubDSectorType::ErrorSectorCoefficient:
sector_theta is not valid.
0 < w < 1:
The returned value is
@@ -5435,7 +5704,7 @@ private:
This is a useful tool when calling AddEdge while a subdivision
level is being constructed.
*/
- static double SectorWeightFromTheta(
+ static double SectorCoefficientFromTheta(
double sector_theta
);
};
@@ -7136,15 +7405,11 @@ protected:
friend class ON_SubDHeap;
enum SavedPointsFlags : unsigned char
{
- // if ( 0 != (m_saved_points_flags & ControlNetFragmentBit), then ON_subDFace.m_control_net_mesh_fragments are set.
- // Otherwise means any information in ON_subDFace.m_control_net_mesh_fragments is invalid and must be recalculated.
- ControlNetFragmentBit = 0x10,
-
- // if ( 0 != (m_saved_points_flags & SubDDisplacementVIsSet), then m_cache_subd_P is set.
- SubdivisionPointBit = 0x20,
-
// if ( 0 != (m_saved_points_flags & SubDDisplacementVIsSet), then m_displacementV is set.
- SubdivisionDisplacementBit = 0x40,
+ SubdivisionDisplacementBit = 0x20,
+
+ // if ( 0 != (m_saved_points_flags & SubdivisionPointBit), then m_cache_subd_P is set.
+ SubdivisionPointBit = 0x40,
// if ( 0 != (m_saved_points_flags & SurfacePointBit), then ON_subDVertex.m_limit* values are set.
// ON_SubDVertex: Set means one or more sector limit surface points are saved in ON_SubDVertex.m_limit_point.
@@ -7157,10 +7422,41 @@ protected:
CachedPointMask = 0xF0
};
+ enum ModifiedFlags : unsigned char
+ {
+ // if ( 0 != (m_saved_points_flags & Modified1Bit), then the component has been modified and
+ // cached subdivision information needs to be recalculated.
+ Modified1Bit = 0x01,
+
+ // if ( 0 != (m_saved_points_flags & Modified2Bit), then the component is adjacent to
+ // a modified component and cached subdivision information needs to be recalculated.
+ Modified2Bit = 0x02,
+
+ // ModifiedFlagsMask = Modified1Bit | Modified2Bit
+ // if ( 0 != (m_saved_points_flags & ModifiedFlagsMask), then any cached subdivision information
+ // on that component needs to be recalculated.
+ ModifiedFlagsMask = 0x03
+ };
+
// m_saved_points_flags is a bit field based on ON_SubDComponentBase::SavePointsFlags values.
// GetSurfacePoint( bUseSavedSurfacePoint=true ) can change the value of m_saved_points_flags
void Internal_SetSavedSurfacePointFlag(bool bSavedSurfacePointFlag) const;
- void Internal_SetSavedControlNetFragmentFlag(bool bSavedControlNetFragmentFlag) const;
+ void Internal_SetModified1Flag() const;
+ void Internal_SetModified2Flag() const;
+
+ /*
+ Returns:
+ True if Modified1Bit or Modified2Bit is set.
+ */
+ bool Internal_Modified1IsSet() const;
+
+ /*
+ Returns:
+ True if Modified1Bit or Modified2Bit is set.
+ */
+ bool Internal_Modified1or2IsSet() const;
+
+ void Internal_ClearModifiedFlags() const;
mutable unsigned char m_saved_points_flags = 0U;
unsigned char m_level = 0U;
@@ -7195,18 +7491,10 @@ protected:
*/
void Internal_ClearSurfacePointFlag() const;
- /*
- Description:
- Clears the flag indicating that ON_SubDFace.m_control_net_fragment is current.
- */
- void Internal_ClearControlNetFragmentFlag() const;
-
bool Internal_SubdivisionPointFlag() const;
bool Internal_SurfacePointFlag() const;
- bool Internal_ControlNetFragmentFlag() const;
-
void Internal_TransformComponentBase(bool bTransformationSavedSubdivisionPoint, const class ON_Xform& xform);
// GetSubdivisionPoint( bUseSavedSubdivisionPoint=true ) can change the value of m_cache_subd_P
mutable double m_saved_subd_point1[3]; // saved subdivision point
@@ -7264,10 +7552,63 @@ public:
public:
static const ON_SubDVertexEdgeProperties Zero; // all member values are zero.
+ /*
+ Returns:
+ True if there are no null edges and there are two edges with a single face and all remaining edges have two faces.
+ Remarks:
+ Tags are ignored.
+ */
+ bool HasInteriorVertexTopology() const;
+
+ /*
+ Returns:
+ True if there are no null edges and there are at least two edges and they all have two faces.
+ Remarks:
+ Tags are ignored.
+ */
+ bool HasBoundaryVertexTopology() const;
+
+ /*
+ Returns:
+ HasInteriorVertexTopology() || HasBoundaryVertexTopology().
+ */
+ bool HasManifoldVertexTopology() const;
+
+ /*
+ Returns:
+ True if there are no null edges and there is an edge with zero faces or an edge with three or more faces.
+ Remarks:
+ Tags are ignored.
+ */
+ bool HasNonmanifoldVertexTopology() const;
+
+ /*
+ Returns:
+ Number of edges.
+ */
+ unsigned EdgeCount() const;
+
public:
// Number of null edges
unsigned short m_null_edge_count = 0;
+
+ /////////////////////////////////////////////////////
+ //
+ // Vertex attached component counts
+ //
+
+ // vertex->m_edge_count
+ unsigned short m_edge_count = 0;
+
+ // vertex->m_face_count
+ unsigned short m_face_count = 0;
+
+ /////////////////////////////////////////////////////
+ //
+ // Edge tag counts
+ //
+
// Number of edges tags ON_SubD::EdgeTag::Unset
unsigned short m_unset_edge_count = 0;
@@ -7277,6 +7618,11 @@ public:
// Number of edges tags ON_SubD::EdgeTag::Crease
unsigned short m_crease_edge_count = 0;
+ /////////////////////////////////////////////////////
+ //
+ // Edge topology counts
+ //
+
// Number of wire edges (0 attached faces)
unsigned short m_wire_edge_count = 0;
@@ -7289,6 +7635,12 @@ public:
// Number of nonmanifold edges (3 or more attached faces)
unsigned short m_nonmanifold_edge_count = 0;
+
+ /////////////////////////////////////////////////////
+ //
+ // Edge face counts
+ //
+
// Minimum value of attached edges's m_face_count.
unsigned short m_min_edge_face_count = 0;
@@ -7589,6 +7941,38 @@ public:
const ON_SubDVertexEdgeProperties EdgeProperties() const;
+ /*
+ Parameters:
+ eptr0 - [out]
+ eptr1 - [out]
+ If a vertex has exactly two attached edges, each of which has a single attached face,
+ then these edges are returned in the order the appear in the vertex's edge list.
+ (RelativeVertex(0) = this vertex). Othwerise the parameters are set to null.
+ Returns:
+ True if the vertex has exactly two attached edges, each of which has a single attached face.
+ False otherwise.
+ */
+ bool GetBoundaryVertexEdges(
+ ON_SubDEdgePtr* eptr0,
+ ON_SubDEdgePtr* eptr1
+ ) const;
+
+ /*
+ Parameters:
+ vei0 - [out]
+ vei1 - [out]
+ If a vertex has exactly two attached edges, each of which has a single attached face,
+ then the indices of those edges in the vertex's edge list are returned.
+ Othewise ON_UNSET_UINT_INDEX is returned.
+ Returns:
+ True if the vertex has exactly two attached edges, each of which has a single attached face.
+ False otherwise.
+ */
+ bool GetBoundaryVertexEdgeIndices(
+ unsigned* vei0,
+ unsigned* vei1
+ ) const;
+
/*
Description:
A "standard" vertex is one where the standard subdivsion matrix for that vertex
@@ -7771,6 +8155,12 @@ public:
*/
bool SurfacePointIsSet() const;
+
+ const ON_Plane VertexFrame(
+ ON_SubDComponentLocation subd_appearance
+ ) const;
+
+
/*
Description:
Call this function if the vertex is modified and it will clear any
@@ -7790,6 +8180,31 @@ public:
*/
unsigned int MarkedFaceCount() const;
+ /*
+ Returns:
+ Minimum number of edges for any face attached to this vertex.
+ */
+ unsigned int MinimumFaceEdgeCount() const;
+
+ /*
+ Returns:
+ Maximum number of edges for any face attached to this vertex.
+ */
+ unsigned int MaximumFaceEdgeCount() const;
+
+
+ /*
+ Returns:
+ Minimum number of faces for any edge attached to this vertex.
+ */
+ unsigned int MinimumEdgeFaceCount() const;
+
+ /*
+ Returns:
+ Maximum number of faces for any edge attached to this vertex.
+ */
+ unsigned int MaximumEdgeFaceCount() const;
+
/*
Description:
Expert user tool to unset ON_SubEdge.m_sector_coefficent[] values for
@@ -7838,6 +8253,8 @@ private:
private:
friend class ON_SubDArchiveIdMap;
+ friend class ON_SubDEdge;
+ friend class ON_SubDFace;
void CopyFrom(
const ON_SubDVertex* src,
bool bCopyEdgeArray,
@@ -7921,6 +8338,11 @@ public:
const ON_BoundingBox ControlNetBoundingBox() const;
+ const ON_Plane CenterFrame(
+ ON_SubDComponentLocation subd_appearance
+ ) const;
+
+
/*
Description:
@@ -7986,12 +8408,12 @@ public:
};
unsigned short m_face_count = 0;
unsigned short m_facex_capacity = 0;
- ON_SubDFacePtr m_face2[2];
+ ON_SubDFacePtr m_face2[2] = {};
ON_SubDFacePtr* m_facex = nullptr;
// m_vertex[0] = vertex at the start of the edge.
// m_vertex[1] = vertex at the end of the edge.
- const class ON_SubDVertex* m_vertex[2];
+ const class ON_SubDVertex* m_vertex[2] = {};
// If the value of vertex->m_vertex_tag is not ON_SubD::VertexTag::Smooth,
// then that vertex is "tagged".
@@ -8012,7 +8434,7 @@ public:
//
// If the value of m_edge_tag is ON_SubD::EdgeTag::Smooth and
// exactly one end vertex is tagged, then the m_sector_coefficient[]
- // value for the tagged end is calculated by ON_SubDSectorType::SectorWeight().
+ // value for the tagged end is calculated by ON_SubDSectorType::SectorCoefficient().
// tagged_weight*tagged_vertex + (1.0 - tagged_weight)*untagged_vertex
// is used when combining the edge ends.
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth
@@ -8021,7 +8443,7 @@ public:
// If the value of m_edge_tag is ON_SubD::EdgeTag::SmoothX, then the edge
// must have exactly two neighboring faces,
// both vertices must be tagged and the m_sector_coefficient[]
- // values are calculated by ON_SubDSectorType::SectorWeight().
+ // values are calculated by ON_SubDSectorType::SectorCoefficient().
// When the edge is subdivided, the midpoint of the edge is the
// location of the edge.s subdivision point.
// The edge's subdivision vertex will be tagged as ON_SubD::VertexTag::Smooth
@@ -8037,8 +8459,12 @@ public:
//
// m_sector_coefficient[tagged_end] = 1/2 + 1/3*cos(theta)
// where "theta" = tagged end "theta" (which depends on vertex tag (dart/crease/corner),
- // the number of faces in the sector, and the crease angle when the tagged end is a corner).
- double m_sector_coefficient[2];
+ // the number of faces in the sector, and the control net crease angle when the tagged end is a corner.
+ //
+ // The name "sector coefficient" is used because the value is a property of the
+ // vertex's sector (every smooth edge inside a vertex sector has the same value at the tagged vertex).
+ // The sector coefficient does not change which a subdivision is applied.
+ double m_sector_coefficient[2] = {};
// If m_edge_tag is not ON_SubD::EdgeTag::Sharp, then m_sharpness is ignored.
// If m_edge_tag is ON_SubD::EdgeTag::Sharp, then m_sharpness controls how hard/soft
@@ -8207,6 +8633,8 @@ public:
*/
const ON_3dPoint ControlNetPoint( unsigned int i ) const;
+ const ON_Line ControlNetLine() const;
+
/*
Returns:
If vertices are set, then the vector from m_vertex[0]->ControlNetPoint()
@@ -8451,6 +8879,8 @@ private:
private:
friend class ON_SubDArchiveIdMap;
+ friend class ON_SubDVertex;
+ friend class ON_SubDFace;
void CopyFrom(
const ON_SubDEdge* src,
bool bReverseEdge,
@@ -8981,6 +9411,9 @@ public:
private:
friend class ON_SubDArchiveIdMap;
+ friend class ON_SubDVertex;
+ friend class ON_SubDEdge;
+
void CopyFrom(
const ON_SubDFace* src,
bool bCopyEdgeArray
@@ -11411,8 +11844,7 @@ public:
//
// If the facet type is quad, and C is a standard interior vertex,
// then the "standard vertex ring" is the list of 2*N+1 points
- // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the average of the
- // four corners of the quad F[i].
+ // (C, P[0], Q[0], ...., P[N-1], Q[N-1]), where Q[I] is the vertex of quad F[i] diagonally across from C.
//
// If the facet type is quad, and C is a standard boundary vertex,
// then the "standard vertex ring" is the list of 2*N points
@@ -11668,6 +12100,18 @@ public:
const ON_SubDEdge* edge0
);
+ /*
+ Description:
+ Allocate a vertex located at the edge0 subdivision point.
+ The vertex will have an edge and face capacity of 4.
+ Parameters:
+ edge0 - [in]
+ */
+ ON_SubDVertex* AllocateEdgeSubdivisionVertex(
+ bool bUseFindOrAllocate,
+ const ON_SubDEdge * edge0
+ );
+
/*
Description:
Find or allocate a vertex and the face subdivision point. The vertex will have an
@@ -11706,13 +12150,13 @@ public:
If v0 null or ON_SubD::VertexTag::Smooth == v0->m_vertex_tag, and v1 is null or tagged,
then m_sector_weight[0] is set to v0_sector_weight.
In all other cases the value of v0_sector_weight is ignored and m_sector_weight[0]
- is set to ON_SubDSectorType::IgnoredSectorWeight.
+ is set to ON_SubDSectorType::IgnoredSectorCoefficient.
v1 - [in]
v1_sector_weight - [in]
If v1 null or ON_SubD::VertexTag::Smooth == v1->m_vertex_tag, and v0 is null or tagged,
then m_sector_weight[1] is set to v1_sector_weight.
In all other cases the value of v1_sector_weight is ignored and m_sector_weight[1]
- is set to ON_SubDSectorType::IgnoredSectorWeight.
+ is set to ON_SubDSectorType::IgnoredSectorCoefficient.
Returns:
An edge.
The vertex parameter information is used to set the ON_SubDEdge.m_vertex[]
@@ -11743,6 +12187,16 @@ public:
double v1_sector_weight
);
+ const ON_SubDEdgePtr AllocateEdge(
+ bool bUseFindOrAllocatEdge,
+ ON_SubDVertex* v0,
+ double v0_sector_weight,
+ ON_SubDVertex* v1,
+ double v1_sector_weight
+ );
+
+
+
private:
/*
Returns:
@@ -11870,33 +12324,80 @@ public:
unsigned int minimum_chain_length - [in]
minimum number of edges to consider for a chain.
- sorted_edges - [out]
- The sorted_edges[] has the edges grouped into edge chains.
+ edge_chains - [out]
+ The edge_chains[] has the edges grouped into edge chains.
In an edge chain subsequent edges share a common vertex; i.e.
- sorted_edges[i].RelativeVertex(1) == sorted_edges[i+1].RelativeVertex(0).
+ edge_chains[i].RelativeVertex(1) == edge_chains[i+1].RelativeVertex(0).
- When sorted_edges[i].RelativeVertex(1) != sorted_edges[i+1].RelativeVertex(0),
- a chain ends at sorted_edges[i] and another begins at sorted_edges[i+1].
+ When edge_chains[i].RelativeVertex(1) != edge_chains[i+1].RelativeVertex(0),
+ a chain ends at edge_chains[i] and another begins at edge_chains[i+1].
The first edge in every chain has the same orientation as the input edge
- from unsorted_edges[].
+ from edge_chains[].
Returns:
- Number of chains in sorted_edges[].
+ Number of chains in edge_chains[].
*/
static unsigned int SortEdgesIntoEdgeChains(
const ON_SimpleArray< ON_SubDEdgePtr >& unsorted_edges,
unsigned int minimum_chain_length,
- ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
);
static unsigned int SortEdgesIntoEdgeChains(
const ON_SimpleArray< const ON_SubDEdge* >& unsorted_edges,
unsigned int minimum_chain_length,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+ );
+
+ static unsigned int SortEdgesIntoEdgeChains(
+ const ON_SimpleArray< ON_SubDComponentPtr >& unsorted_edges,
+ unsigned int minimum_chain_length,
ON_SimpleArray< ON_SubDEdgePtr >& sorted_edges
);
+ static unsigned int SortEdgesIntoEdgeChains(
+ const ON_SubD& subd,
+ const ON_SimpleArray< ON_COMPONENT_INDEX >& unsorted_edges,
+ unsigned int minimum_chain_length,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+ );
+
+ /*
+ Description:
+ Orient edges[] into edge chains preserving the order of edges[].
+ Returns:
+ Number of chains in edge_chains[].
+ */
+ static unsigned int OrientEdgesIntoEdgeChains(
+ const ON_SubD& subd,
+ const ON_SimpleArray< ON_COMPONENT_INDEX >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+ );
+
+ /*
+ Description:
+ Orient edges[] into edge chains preserving the order of edges[].
+ Returns:
+ Number of chains in edge_chains[].
+ */
+ static unsigned int OrientEdgesIntoEdgeChains(
+ const ON_SimpleArray< const ON_SubDEdge* >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+ );
+
+ /*
+ Description:
+ Orient edges[] into edge chains preserving the order of edges[].
+ Returns:
+ Number of chains in edge_chains[].
+ */
+ static unsigned int OrientEdgesIntoEdgeChains(
+ const ON_SimpleArray< ON_SubDComponentPtr >& edges,
+ ON_SimpleArray< ON_SubDEdgePtr >& edge_chains
+ );
+
/////////////////////////////////////////////////////////
//
@@ -12159,6 +12660,222 @@ private:
ON_ComponentStatus m_status_check_fail = ON_ComponentStatus::Selected;
};
+class ON_CLASS ON_SubDComponentFilter
+{
+public:
+ ON_SubDComponentFilter() = default;
+ ~ON_SubDComponentFilter() = default;
+ ON_SubDComponentFilter(const ON_SubDComponentFilter&) = default;
+ ON_SubDComponentFilter& operator=(const ON_SubDComponentFilter&) = default;
+
+public:
+
+ ///
+ /// No filters are set and all components are accepted.
+ ///
+ static const ON_SubDComponentFilter Unset;
+
+ ///
+ /// Only vertices are accepted.
+ ///
+ static const ON_SubDComponentFilter OnlyVertices;
+
+ ///
+ /// Only edges are accepted.
+ ///
+ static const ON_SubDComponentFilter OnlyEdges;
+
+ ///
+ /// Only faces are accepted.
+ ///
+ static const ON_SubDComponentFilter OnlyFaces;
+
+ /*
+ Parameters:
+ bAcceptVertices - [in]
+ If true, all vertices are accepted. Otherwise, all vertices are rejected.
+ bAcceptEdges - [in]
+ If true, all edges are accepted. Otherwise all edges are rejected.
+ bAcceptFaces - [in]
+ If true, all faces are accepted. Otherwise all faces are rejected.
+ */
+ static const ON_SubDComponentFilter Create(
+ bool bAcceptVertices,
+ bool bAcceptEdges,
+ bool bAcceptFaces
+ );
+
+public:
+ ///
+ /// Topology filters.
+ ///
+ enum class Topology : unsigned char
+ {
+ ///
+ /// No topology filter.
+ ///
+ Unset = 0,
+
+ ///
+ /// A boundary vertex has a single sector bounded by two boundary edges.
+ /// A boundary edge has a single face.
+ /// A boundary face has at least one boundary edge.
+ ///
+ Boundary = 1,
+
+ ///
+ /// An interior vertex has the same number of edges and faces and all edges are interior.
+ /// An interior edge has two faces.
+ /// An interior face has all interior edges.
+ ///
+ Interior = 2,
+
+ ///
+ /// A nonmanifold vertex is a vertex that is neither boundary nor interior.
+ /// A nonmanifold edge is an edge that is neither boundary nor interior.
+ /// A nonmanifold face is a face that is neither boundary nor interior.
+ ///
+ Nonmanifold = 4,
+
+ ///
+ /// A component that is either boundary or interior.
+ ///
+ BoundaryOrInterior = 3,
+
+ ///
+ /// A component that is either boundary or nonmanifold.
+ ///
+ BoundaryOrNonmanifold = 5,
+
+ ///
+ /// A component that is either interior or nonmanifold
+ ///
+ InteriorOrNonmanifold = 6
+ };
+
+ bool AcceptComponent(
+ const class ON_Geometry* geometry
+ ) const;
+
+ bool AcceptComponent(
+ ON_COMPONENT_INDEX component_index,
+ const class ON_Geometry* geometry
+ ) const;
+
+ bool AcceptComponent(
+ const class ON_SubDComponentRef* cref
+ ) const;
+
+ /*
+ Returns:
+ True if the filter accepts the component. False otherwise.
+ */
+ bool AcceptComponent(ON_SubDComponentPtr cptr) const;
+
+ /*
+ Returns:
+ True if the filter accepts the vertex. False otherwise.
+ */
+ bool AcceptVertex(ON_SubDVertexPtr vptr) const;
+
+ /*
+ Returns:
+ True if the filter accepts the edge. False otherwise.
+ */
+ bool AcceptEdge(ON_SubDEdgePtr eptr) const;
+
+ /*
+ Returns:
+ True if the filter accepts the face. False otherwise.
+ */
+ bool AcceptFace(ON_SubDFacePtr fptr) const;
+
+ /*
+ Returns:
+ True if the filter accepts the vertex. False otherwise.
+ */
+ bool AcceptVertex(const ON_SubDVertex* v) const;
+
+ /*
+ Returns:
+ True if the filter accepts the edge. False otherwise.
+ */
+ bool AcceptEdge(const ON_SubDEdge* e) const;
+
+ /*
+ Returns:
+ True if the filter accepts the face. False otherwise.
+ */
+ bool AcceptFace(const ON_SubDFace* f) const;
+
+ void SetAcceptVertices(bool bAcceptVertices);
+
+ bool AcceptVertices() const;
+
+ void SetAcceptEdges(bool bAcceptEdges);
+
+ bool AcceptEdges() const;
+
+ void SetAcceptFaces(bool bAcceptFaces);
+
+ bool AcceptFaces() const;
+
+ void SetVertexTopologyFilter(ON_SubDComponentFilter::Topology vertex_topology_filter);
+
+ void ClearVertexTopologyFilter();
+
+ ON_SubDComponentFilter::Topology VertexTopologyFilter() const;
+
+ void SetEdgeTopologyFilter(ON_SubDComponentFilter::Topology edge_topology_filter);
+
+ ON_SubDComponentFilter::Topology EdgeTopologyFilter() const;
+
+ void ClearEdgeTopologyFilter();
+
+ void SetFaceTopologyFilter(ON_SubDComponentFilter::Topology face_topology_filter);
+
+ ON_SubDComponentFilter::Topology FaceTopologyFilter() const;
+
+ void ClearFaceTopologyFilter();
+
+ bool AcceptVertexTag(ON_SubD::VertexTag vertex_tag) const;
+
+ void AddAcceptedVertexTag(ON_SubD::VertexTag vertex_tag);
+
+ void ClearVertexTagFilter();
+
+ bool AcceptEdgeTag(ON_SubD::EdgeTag edge_tag) const;
+
+ void AddAcceptedEdgeTag(ON_SubD::EdgeTag edge_tag);
+
+ void ClearEdgeTagFilter();
+
+ bool AcceptFaceEdgeCount(
+ unsigned face_edge_count
+ ) const;
+
+ void SetFaceEdgeCountFilter(
+ unsigned minimum_face_edge_count,
+ unsigned maximum_face_edge_count
+ );
+
+ void ClearFaceEdgeCountFilter();
+
+private:
+ bool m_bRejectVertices = false;
+ ON_SubDComponentFilter::Topology m_vertex_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+ ON_SubD::VertexTag m_vertex_tag_filter[4] = {};
+
+ bool m_bRejectEdges = false;
+ ON_SubDComponentFilter::Topology m_edge_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+ ON_SubD::EdgeTag m_edge_tag_filter[2] = {};
+
+ bool m_bRejectFaces = false;
+ ON_SubDComponentFilter::Topology m_face_topology_filter = ON_SubDComponentFilter::Topology::Unset;
+ unsigned m_minimum_face_edge_count = 0U;
+ unsigned m_maximum_face_edge_count = 0U;
+};
+
#if defined(ON_COMPILING_OPENNURBS)
/*
diff --git a/opennurbs_subd_archive.cpp b/opennurbs_subd_archive.cpp
index b0e615ca..6d69b795 100644
--- a/opennurbs_subd_archive.cpp
+++ b/opennurbs_subd_archive.cpp
@@ -1417,7 +1417,7 @@ bool ON_SubDimple::Write(
{
const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId();
- const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 1;
+ const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 2;
if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version) )
return ON_SUBD_RETURN_ERROR(false);
bool rc = false;
@@ -1471,6 +1471,10 @@ bool ON_SubDimple::Write(
if (false == m_texture_mapping_tag.Write(archive))
break;
+ // minor version = 2 addtions
+ if (false == m_symmetry.Write(archive))
+ break;
+
rc = true;
break;
}
@@ -1555,6 +1559,12 @@ bool ON_SubDimple::Read(
if (false == m_texture_mapping_tag.Read(archive))
break;
+
+ if (minor_version >= 2)
+ {
+ if (false == m_symmetry.Read(archive))
+ break;
+ }
}
rc = true;
diff --git a/opennurbs_subd_copy.cpp b/opennurbs_subd_copy.cpp
index 6e6936d1..88e9abf5 100644
--- a/opennurbs_subd_copy.cpp
+++ b/opennurbs_subd_copy.cpp
@@ -619,6 +619,8 @@ ON_SubD& ON_SubD::operator=(const ON_SubD& src)
{
this->Destroy();
this->CopyHelper(src);
+ // The next line copies user data
+ ON_Geometry::operator=(src);
}
return *this;
}
@@ -706,8 +708,9 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src)
continue;
if (nullptr == src_level->m_edge[0])
continue;
- if (nullptr == src_level->m_face[0])
- continue;
+ // it's ok to have subd with just vertices and edges.
+ //NO//if (nullptr == src_level->m_face[0])
+ //NO// continue;
break;
}
@@ -732,6 +735,10 @@ ON_SubDimple::ON_SubDimple(const ON_SubDimple& src)
m_max_face_id = src.m_max_face_id;
m_subd_appearance = src.m_subd_appearance;
+ m_texture_domain_type = src.m_texture_domain_type;
+ m_texture_mapping_tag = src.m_texture_mapping_tag;
+ m_symmetry = src.m_symmetry;
+
ChangeContentSerialNumber();
}
diff --git a/opennurbs_subd_data.cpp b/opennurbs_subd_data.cpp
index 333483e6..96cd3dc3 100644
--- a/opennurbs_subd_data.cpp
+++ b/opennurbs_subd_data.cpp
@@ -832,6 +832,15 @@ bool ON_SubDimple::Transform(
}
+ if (m_symmetry.IsSet())
+ {
+ m_symmetry = m_symmetry.TransformConditionally(xform);
+ }
+ else
+ {
+ m_symmetry = ON_Symmetry::Unset;
+ }
+
return rc;
}
diff --git a/opennurbs_subd_data.h b/opennurbs_subd_data.h
index aa066e55..d3ae0565 100644
--- a/opennurbs_subd_data.h
+++ b/opennurbs_subd_data.h
@@ -469,22 +469,19 @@ void ON_SubDIncrementErrorCount(); // defined in opennurbs_subd.cpp
//
// m_saved_points_flags
//
-#define ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::ControlNetFragmentBit
#define ON_SUBD_CACHE_POINT_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SubdivisionPointBit
#define ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SubdivisionDisplacementBit
#define ON_SUBD_CACHE_LIMITLOC_FLAG_BIT ON_SubDComponentBase::SavedPointsFlags::SurfacePointBit
#define ON_SUBD_CACHE_FLAGS_MASK ON_SubDComponentBase::SavedPointsFlags::CachedPointMask
#define ON_SUBD_CACHE_FLAGS(cache_subd_flags) (ON_SUBD_CACHE_FLAGS_MASK&(cache_subd_flags))
-#define ON_SUBD_CACHE_CTRLNETFRAG_FLAG(cache_subd_flags) (ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT&(cache_subd_flags))
#define ON_SUBD_CACHE_POINT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_POINT_FLAG_BIT&(cache_subd_flags))
#define ON_SUBD_CACHE_DISPLACEMENT_FLAG(cache_subd_flags) (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT&(cache_subd_flags))
#define ON_SUBD_CACHE_LIMITLOC_FLAG(cache_subd_flags) (ON_SUBD_CACHE_LIMITLOC_FLAG_BIT&(cache_subd_flags))
-#define ON_SUBD_CACHE_CLEAR_CTRLNETFRAG_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT))
-#define ON_SUBD_CACHE_CLEAR_POINT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT))
-#define ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT))
-#define ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_CTRLNETFRAG_FLAG_BIT))
+#define ON_SUBD_CACHE_CLEAR_POINT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask))
+#define ON_SUBD_CACHE_CLEAR_DISPLACEMENT_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_LIMITLOC_FLAG_BIT|ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask))
+#define ON_SUBD_CACHE_CLEAR_LIMITLOC_FLAG(cache_subd_flags) (cache_subd_flags &= (ON_SUBD_CACHE_POINT_FLAG_BIT|ON_SUBD_CACHE_DISPLACEMENT_FLAG_BIT|ON_SubDComponentBase::ModifiedFlags::ModifiedFlagsMask))
//////////////////////////////////////////////////////////////////////////
@@ -1164,6 +1161,8 @@ public:
void ClearEvaluationCache() const;
+ void ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const;
+
void ClearTopologicalAttributes() const
{
m_aggregates.ClearTopologicalAttributes();
@@ -1947,7 +1946,7 @@ public:
double sin_angle_tolerance
);
- ON_SubDEdgePtr MergeEdges(
+ ON_SubDEdgePtr MergeConsecutiveEdges(
ON_SubDEdgePtr eptr0,
ON_SubDEdgePtr eptr1
);
@@ -2097,6 +2096,12 @@ private:
);
+private:
+ ON_Symmetry m_symmetry;
+
+public:
+
+
private:
ON_SubDimple& operator=(const ON_SubDimple&) = delete;
diff --git a/opennurbs_subd_eval.cpp b/opennurbs_subd_eval.cpp
index f188897f..c676af67 100644
--- a/opennurbs_subd_eval.cpp
+++ b/opennurbs_subd_eval.cpp
@@ -100,7 +100,7 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD(
sector_angle_radians = ON_UNSET_UINT_INDEX;
}
- const double smooth_edge_w0 = this->SectorWeight();
+ const double smooth_edge_w0 = this->SectorCoefficient();
ON_SimpleArray< ON_SubDVertex* > V(R);
ON_SimpleArray< ON_SubDEdge* > E(N);
@@ -151,13 +151,13 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD(
else
edge_tag_vei = ON_SubD::EdgeTag::Smooth; // interior edge
- double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorWeight;
+ double w0 = (ON_SubD::EdgeTag::Smooth == edge_tag_vei) ? smooth_edge_w0 : ON_SubDSectorType::IgnoredSectorCoefficient;
unsigned int ev1i = 1 + vei*ring_ei_delta;
E.Append(
subd->AddEdgeWithSectorCoefficients(
edge_tag_vei,
V[0], w0,
- V[ev1i], ON_SubDSectorType::IgnoredSectorWeight)
+ V[ev1i], ON_SubDSectorType::IgnoredSectorCoefficient)
);
}
@@ -178,8 +178,8 @@ ON_SubD* ON_SubDSectorType::SectorRingSubD(
f_edgeptr[0] = ON_SubDEdgePtr::Create(f_edge[0], 0);
f_edgeptr[3] = ON_SubDEdgePtr::Create(f_edge[3], 1);
f_vertex[2] = V[2 + 2 * vfi];
- f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight);
- f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorWeight, f_vertex[3], ON_SubDSectorType::IgnoredSectorWeight);
+ f_edge[1] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[1], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient);
+ f_edge[2] = subd->AddEdgeWithSectorCoefficients(ON_SubD::EdgeTag::Smooth, f_vertex[2], ON_SubDSectorType::IgnoredSectorCoefficient, f_vertex[3], ON_SubDSectorType::IgnoredSectorCoefficient);
f_edgeptr[1] = ON_SubDEdgePtr::Create(f_edge[1], 0);
f_edgeptr[2] = ON_SubDEdgePtr::Create(f_edge[2], 0);
subd->AddFace(f_edgeptr,4);
@@ -431,10 +431,14 @@ double ON_SubDMatrix::TestEvaluation() const
static bool GetSectorLimitPointHelper(
const ON_SubDSectorIterator& sit,
- bool bUndefinedNormalIsPossible,
+ bool& bUndefinedNormalIsPossible,
ON_SubDSectorSurfacePoint& limit_point
)
{
+ limit_point.m_limitP[0] = ON_DBL_QNAN;
+ limit_point.m_limitP[1] = ON_DBL_QNAN;
+ limit_point.m_limitP[2] = ON_DBL_QNAN;
+
const ON_SubDSectorType sector_type = ON_SubDSectorType::Create(sit);
if (false == sector_type.IsValid())
return ON_SUBD_RETURN_ERROR(false);
@@ -467,9 +471,32 @@ static bool GetSectorLimitPointHelper(
if (R != SM.m_R || nullptr == SM.m_LP)
break;
+ if (
+ false == bUndefinedNormalIsPossible
+ && ON_SubD::VertexTag::Crease == SM.m_sector_type.VertexTag()
+ && R >= 5
+ && *((const ON_3dPoint*)(point_ring+ point_ring_stride)) == *((const ON_3dPoint*)(point_ring + (R-1)* point_ring_stride))
+ )
+ {
+ // Crease where ends of the creased edges are equal.
+ // common overlapping creases (happens when a smooth interior edge is separated (unwelded) into two creases)
+ bUndefinedNormalIsPossible = true;
+ }
+
if (false == SM.EvaluateSurfacePoint(point_ring, R, point_ring_stride, bUndefinedNormalIsPossible, limit_point))
break;
+ if (
+ false == bUndefinedNormalIsPossible
+ && 0.0 == limit_point.m_limitN[0] && 0.0 == limit_point.m_limitN[1] && 0.0 == limit_point.m_limitN[2]
+ && limit_point.IsSet(true)
+ )
+ {
+ // SM.EvaluateSurfacePoint() logged the error - setting bUndefinedNormalIsPossible = true here
+ // allows the limit point to be cached so the same error doesn't continue to get logged.
+ bUndefinedNormalIsPossible = true;
+ }
+
rc = true;
break;
}
@@ -591,19 +618,12 @@ bool ON_SubDVertex::GetSurfacePoint(
// cache does not contain this limit point.
}
-
if (nullptr == (nullptr == sector_face ? sit.Initialize(this) : sit.Initialize(sector_face, 0, this)))
{
limit_point = ON_SubDSectorSurfacePoint::Unset;
return ON_SUBD_RETURN_ERROR(false);
}
- if (nullptr == sit.Initialize(sector_face,0,this))
- {
- limit_point = ON_SubDSectorSurfacePoint::Unset;
- return ON_SUBD_RETURN_ERROR(false);
- }
-
limit_point_sector_face = sit.IncrementToCrease(-1);
rc = GetSectorLimitPointHelper( sit, bUndefinedNormalIsPossible, limit_point);
@@ -644,6 +664,117 @@ const ON_3dPoint ON_SubDVertex::SurfacePoint() const
}
+
+const ON_Plane ON_SubDVertex::VertexFrame(
+ ON_SubDComponentLocation subd_appearance
+) const
+{
+ if (0 == FaceCount())
+ return ON_Plane::NanPlane;
+
+ const ON_SubDFace* sector_face = Face(0);
+ if (nullptr == sector_face)
+ return ON_Plane::NanPlane;
+
+ ON_Plane vertex_frame(ON_Plane::NanPlane);
+ if (ON_SubDComponentLocation::ControlNet == subd_appearance)
+ {
+ ON_3dVector V = ON_3dVector::ZeroVector;
+ for (int vei = 0; vei < m_edge_count; ++vei)
+ {
+ const ON_SubDEdge* e = Edge(vei);
+ if (nullptr == e)
+ continue;
+ const ON_SubDVertex* v1 = e->OtherEndVertex(this);
+ if (nullptr == v1)
+ continue;
+ const ON_SubDFace* f = (1 == e->m_face_count) ? e->Face(0) : nullptr;
+ if (nullptr == f)
+ continue;
+ sector_face = f;
+ V = (v1->ControlNetPoint() - ControlNetPoint()).UnitVector();
+ break;
+ }
+ vertex_frame.CreateFromNormal(ControlNetPoint(), sector_face->ControlNetCenterNormal());
+ const ON_3dVector X = (V - (V * vertex_frame.zaxis) * vertex_frame.zaxis).UnitVector();
+ if (X.IsUnitVector())
+ {
+ vertex_frame.xaxis = X;
+ vertex_frame.yaxis = ON_CrossProduct(vertex_frame.zaxis, vertex_frame.xaxis).UnitVector();
+ }
+ }
+ else
+ {
+ // If this is a smooth vertex or a crease vertex on the boundary,
+ // then the sector_face does not matter. Otherwise it picks the
+ // "side of the crease" for the normal.
+ ON_SubDSectorSurfacePoint limit_point;
+ if (FaceCount())
+ if (false == GetSurfacePoint(sector_face, limit_point))
+ return ON_Plane::NanPlane;
+
+ ON_3dVector Y(ON_CrossProduct(limit_point.m_limitN, limit_point.m_limitT1));
+ Y.Unitize();
+
+ // The normal is more important than the tangent direction.
+ vertex_frame.CreateFromNormal(ON_3dPoint(limit_point.m_limitP), ON_3dVector(limit_point.m_limitN));
+ vertex_frame.yaxis = Y;
+ vertex_frame.xaxis = ON_CrossProduct(vertex_frame.yaxis, vertex_frame.zaxis);
+ vertex_frame.xaxis.Unitize();
+ }
+
+ return vertex_frame.IsValid() ? vertex_frame : ON_Plane::NanPlane;
+}
+
+
+const ON_Plane ON_SubDEdge::CenterFrame(
+ ON_SubDComponentLocation subd_appearance
+) const
+{
+ // to fix RH-41763, get the limit mesh fragment for an attached face
+ // and use subd.LimitSurfaceMesh().GetEdgeCenterPointAndNormal()
+ // to get P and N.
+ ON_Plane edge_frame(ON_Plane::NanPlane);
+ ON_3dPoint center_point(ON_3dPoint::NanPoint);
+ ON_3dVector center_normal(ON_3dVector::NanVector);
+ bool rc = false;
+ // surface center and normal are not available in public opennurbs
+ center_point = ControlNetCenterPoint();
+ center_normal = ControlNetCenterNormal(0);
+ rc = center_point.IsValid() && center_normal.IsUnitVector();
+ if (rc)
+ {
+ if (false == edge_frame.CreateFromNormal(center_point, center_normal))
+ return ON_Plane::NanPlane;
+ const ON_3dVector U = ControlNetDirection();
+ ON_2dVector v(U * edge_frame.xaxis, U * edge_frame.yaxis);
+ if ( v.Unitize() )
+ {
+ if (fabs(v.y) > ON_SQRT_EPSILON&& fabs(v.x) < (1.0 - ON_SQRT_EPSILON))
+ {
+ const ON_3dVector X = (v.x * edge_frame.xaxis + v.y * edge_frame.yaxis).UnitVector();
+ if (X.IsUnitVector())
+ {
+ const ON_3dVector Y = ON_CrossProduct(edge_frame.zaxis, X).UnitVector();
+ if (Y.UnitVector())
+ {
+ edge_frame.xaxis = X;
+ edge_frame.yaxis = Y;
+ }
+ }
+ }
+ else if (v.x < 0.0)
+ {
+ edge_frame.xaxis = -edge_frame.xaxis;
+ edge_frame.yaxis = -edge_frame.yaxis;
+ }
+ }
+ }
+ return edge_frame.IsValid() ? edge_frame : ON_Plane::NanPlane;
+}
+
+
+
const ON_3dPoint ON_SubDVertex::Point(ON_SubDComponentLocation point_location) const
{
switch (point_location)
@@ -823,10 +954,11 @@ void ON_SubDVertex::ClearSavedSurfacePoints() const
{
// clear vertex specific cache
Internal_ClearSurfacePointFlag();
- if (ON_UNSET_VALUE != m_limit_point.m_limitP[0] && nullptr != m_limit_point.m_sector_face)
+ if (nullptr != m_limit_point.m_next_sector_limit_point)
{
// return multiple sector limit points to pool
const ON_SubDSectorSurfacePoint* next_p = m_limit_point.m_next_sector_limit_point;
+ m_limit_point.m_next_sector_limit_point = nullptr;
for (const ON_SubDSectorSurfacePoint* p = next_p; nullptr != p; p = next_p)
{
next_p = p->m_next_sector_limit_point;
diff --git a/opennurbs_subd_fragment.cpp b/opennurbs_subd_fragment.cpp
index d5424362..3d0c07e7 100644
--- a/opennurbs_subd_fragment.cpp
+++ b/opennurbs_subd_fragment.cpp
@@ -1201,13 +1201,17 @@ bool ON_SubDMeshFragment::Internal_GetFrameHelper(
if (P.IsValid() && Z.IsNotZero())
{
const ON_3dPoint Q(VertexPoint(Q_dex));
- ON_3dVector V = (Q - P).UnitVector();
- ON_3dVector X = (V - (frame.zaxis*V)*V).UnitVector();
- if (X.IsUnitVector())
+ const ON_3dVector V = (Q - P).UnitVector();
+ const ON_3dVector X = (V - (Z*V)*Z).UnitVector();
+ const ON_3dVector Y = ON_CrossProduct(Z, X).UnitVector();
+ if ( X.IsUnitVector() && Y.IsUnitVector()
+ && fabs(X*Z) <= ON_SQRT_EPSILON
+ && fabs(Y*Z) <= ON_SQRT_EPSILON
+ )
{
frame.origin = P;
frame.xaxis = X;
- frame.yaxis = ON_CrossProduct(Z, X);
+ frame.yaxis = Y;
frame.zaxis = Z;
frame.UpdateEquation();
}
diff --git a/opennurbs_subd_frommesh.cpp b/opennurbs_subd_frommesh.cpp
index b2983642..64f0486a 100644
--- a/opennurbs_subd_frommesh.cpp
+++ b/opennurbs_subd_frommesh.cpp
@@ -418,6 +418,13 @@ ON_SubD* ON_SubD::CreateFromMesh(
i0++;
i1--;
}
+ // Flip middle edge if odd number of edges
+ if (i0 == i1)
+ {
+ int k = mesh_edges[i0].i;
+ mesh_edges[i0].i = mesh_edges[i0].j;
+ mesh_edges[i0].j = k;
+ }
}
// the ngon created a single subd face
@@ -673,8 +680,8 @@ ON_SubD* ON_SubD::CreateFromMesh(
// Later, some of the ON_SubD::EdgeTag::Smooth tags are changed to ON_SubD::EdgeTag::Crease or ON_SubD::EdgeTag::SmoothX.
mesh_edge.e
= (mesh_edge.i <= mesh_edge.j)
- ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight)
- : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorWeight, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorWeight);
+ ? new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient)
+ : new_subd->AddEdgeWithSectorCoefficients(mesh_edge.edge_tag, V[mesh_edge.j], ON_SubDSectorType::IgnoredSectorCoefficient, V[mesh_edge.i], ON_SubDSectorType::IgnoredSectorCoefficient);
mesh_edges[mesh_edge_map[i]].e = mesh_edge.e;
for (i++; i < mesh_edges.UnsignedCount(); i++)
{
@@ -807,7 +814,7 @@ ON_SubD* ON_SubD::CreateFromMesh(
if (tagged_end_index < 2)
{
// sector weight will be calculated when facet type is set
- const_cast(edge)->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::UnsetSectorWeight;
+ const_cast(edge)->m_sector_coefficient[tagged_end_index] = ON_SubDSectorType::UnsetSectorCoefficient;
}
else if (2 == tagged_end_index)
{
@@ -817,8 +824,8 @@ ON_SubD* ON_SubD::CreateFromMesh(
// first subdivision will convert edge to smooth
const_cast(edge)->m_edge_tag = ON_SubD::EdgeTag::SmoothX;
// sector weights will be calculated when facet type is set
- const_cast(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorWeight;
- const_cast(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorWeight;
+ const_cast(edge)->m_sector_coefficient[0] = ON_SubDSectorType::UnsetSectorCoefficient;
+ const_cast(edge)->m_sector_coefficient[1] = ON_SubDSectorType::UnsetSectorCoefficient;
}
else
{
diff --git a/opennurbs_subd_heap.cpp b/opennurbs_subd_heap.cpp
index c9149389..ed3fd6ff 100644
--- a/opennurbs_subd_heap.cpp
+++ b/opennurbs_subd_heap.cpp
@@ -483,6 +483,11 @@ ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateVertex(
return v1;
}
+ON_SubDVertex* ON_SubD_FixedSizeHeap::AllocateEdgeSubdivisionVertex(bool bUseFindOrAllocate, const ON_SubDEdge* edge0)
+{
+ return bUseFindOrAllocate ? FindOrAllocateVertex(edge0) : AllocateVertex(edge0);
+}
+
ON_SubDVertex * ON_SubD_FixedSizeHeap::FindOrAllocateVertex(const ON_SubDEdge * edge0)
{
if ( nullptr == edge0)
@@ -590,6 +595,10 @@ ON_SubDVertex * ON_SubD_FixedSizeHeap::AllocateSectorFaceVertex(const ON_SubDFac
return v1;
}
+const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge(bool bUseFindOrAllocatEdge, ON_SubDVertex* v0, double v0_sector_weight, ON_SubDVertex* v1, double v1_sector_weight)
+{
+ return bUseFindOrAllocatEdge ? FindOrAllocateEdge( v0, v0_sector_weight, v1, v1_sector_weight) : AllocateEdge(v0, v0_sector_weight, v1, v1_sector_weight);
+}
const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::FindOrAllocateEdge(ON_SubDVertex * v0, double v0_sector_weight, ON_SubDVertex * v1, double v1_sector_weight)
{
@@ -641,7 +650,7 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge(
if (ON_SubD::VertexTag::Smooth == v0->m_vertex_tag)
{
bTaggedVertex[0] = false;
- v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
}
else
{
@@ -658,7 +667,7 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge(
if (ON_SubD::VertexTag::Smooth == v1->m_vertex_tag)
{
bTaggedVertex[1] = false;
- v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
}
else
{
@@ -666,18 +675,18 @@ const ON_SubDEdgePtr ON_SubD_FixedSizeHeap::AllocateEdge(
if (bTaggedVertex[0] && bTaggedVertex[1])
{
// crease edge - no weights
- v0_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
- v1_sector_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v0_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
+ v1_sector_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
}
}
}
else
bTaggedVertex[1] = false;
- if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v0_sector_weight, true))
+ if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_sector_weight, true))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
- if ( false == ON_SubDSectorType::IsValidSectorWeightValue(v1_sector_weight, true))
+ if ( false == ON_SubDSectorType::IsValidSectorCoefficientValue(v1_sector_weight, true))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
ON_SubDEdge* e = m_e + m_e_index;
@@ -1779,7 +1788,6 @@ bool ON_SubDHeap::ReturnMeshFragments(const ON_SubDFace * face)
if (nullptr != face)
{
face->Internal_ClearSurfacePointFlag();
- face->Internal_ClearControlNetFragmentFlag();
ON_SubDMeshFragment* fragment = face->m_mesh_fragments;
face->m_mesh_fragments = nullptr;
while (nullptr != fragment)
diff --git a/opennurbs_subd_limit.cpp b/opennurbs_subd_limit.cpp
index c4d86a50..0cc85b23 100644
--- a/opennurbs_subd_limit.cpp
+++ b/opennurbs_subd_limit.cpp
@@ -1705,7 +1705,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight(
return ON_SUBD_RETURN_ERROR(false);
if (ON_SubD::EdgeTag::Smooth != e0->m_edge_tag && ON_SubD::EdgeTag::SmoothX != e0->m_edge_tag )
- return ON_SubDSectorType::IgnoredSectorWeight;
+ return ON_SubDSectorType::IgnoredSectorCoefficient;
if (e0v == e0->m_vertex[0])
return e0->m_sector_coefficient[0];
@@ -1718,6 +1718,7 @@ static double ON_SubDQuadFaceTopology_CopySectorWeight(
static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge(
ON_SubD_FixedSizeHeap& fsh,
+ bool bUseFindOrAllocate,
ON_SubDVertex* qv1,
const ON_SubDVertex* qv0,
const ON_SubDEdge* e0
@@ -1726,7 +1727,7 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge(
if (nullptr == qv1 || nullptr == e0)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
- ON_SubDVertex* v1 = fsh.AllocateVertex(e0);
+ ON_SubDVertex* v1 = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate,e0);
if (nullptr == v1)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
@@ -1737,20 +1738,20 @@ static const ON_SubDEdgePtr ON_SubDQuadFaceTopology_SubdivideEdge(
if ( qv1->m_vertex_tag != qv0->m_vertex_tag )
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
if ( ON_SubD::VertexTag::Smooth == qv0->m_vertex_tag)
- v0_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
else
{
v0_weight = ON_SubDQuadFaceTopology_CopySectorWeight(e0, qv0);
- if (false == ON_SubDSectorType::IsValidSectorWeightValue(v0_weight,false))
+ if (false == ON_SubDSectorType::IsValidSectorCoefficientValue(v0_weight,false))
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
}
}
else
- v0_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ v0_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
- const double v1_weight = ON_SubDSectorType::IgnoredSectorWeight;
+ const double v1_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
- ON_SubDEdgePtr ep1 = fsh.AllocateEdge(qv1,v0_weight,v1,v1_weight);
+ ON_SubDEdgePtr ep1 = fsh.AllocateEdge(bUseFindOrAllocate,qv1,v0_weight,v1,v1_weight);
ON_SubDEdge* e1 = ep1.Edge();
if (nullptr == e1)
return ON_SUBD_RETURN_ERROR(ON_SubDEdgePtr::Null);
@@ -1796,9 +1797,9 @@ static ON_SubDFace* ON_SubDQuadFaceTopology_SubdivideFace(
if ( nullptr == v[2])
return ON_SUBD_RETURN_ERROR(nullptr);
- const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight;
- const double v2_weight = ON_SubDSectorType::IgnoredSectorWeight;
- const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorWeight;
+ const double v1_weight = (ON_SubD::VertexTag::Crease == v[1]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient;
+ const double v2_weight = ON_SubDSectorType::IgnoredSectorCoefficient;
+ const double v3_weight = (ON_SubD::VertexTag::Crease == v[3]->m_vertex_tag) ? at_crease_weight : ON_SubDSectorType::IgnoredSectorCoefficient;
ON_SubDEdgePtr e12 = fsh.AllocateEdge(v[1],v1_weight,v[2],v2_weight);
if ( nullptr == e12.Edge())
@@ -1833,7 +1834,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
// of a creased original edge, this is the value to assign to the new
// edge's m_vertex_weight. The "2" is there because there would be 2
// sector faces if the subdivision was complete.
- const double at_crease_weight = ON_SubDSectorType::CreaseSectorWeight(2);
+ const double at_crease_weight = ON_SubDSectorType::CreaseSectorCoefficient(2);
if ( m_fsh == q1ft->m_fsh)
q1ft->m_fsh = nullptr;
@@ -1892,6 +1893,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if (N < ON_SubDSectorType::MinimumSectorEdgeCount(qv0->m_vertex_tag))
return ON_SUBD_RETURN_ERROR(false);
+ // When qv0 is a valence 2 vertex with trianglar faces, we need to use find or allocate.
+ const bool bUseFindOrAllocate = (2 == N && 3 == qv0->MinimumFaceEdgeCount());
+
ON_SubDSectorIterator sit;
if ( nullptr == sit.Initialize(qf0,0,qv0) )
return ON_SUBD_RETURN_ERROR(false);
@@ -1931,7 +1935,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
ON_SubDEdgePtr edge_grid1[4][2] = {};
// edge1 = new edge from qv1 to edge0 subdivision point
- ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh,qv1,qv0,edge0);
+ ON_SubDEdgePtr edgep1 = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate,qv1,qv0,edge0);
if (edgep1.IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -1955,7 +1959,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if (edge0 == e0[1])
e1[1] = edgep1; // back to where we started
else
- e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[1]);
+ e1[1] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate, qv1, qv0, e0[1]);
if (e1[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -2067,7 +2071,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if (nullptr == e0[0])
return ON_SUBD_RETURN_ERROR(false);
e1[1] = e1[0];
- e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, qv1, qv0, e0[0]);
+ e1[0] = ON_SubDQuadFaceTopology_SubdivideEdge(fsh, bUseFindOrAllocate, qv1, qv0, e0[0]);
if (e1[0].IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -2107,7 +2111,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
// Add the 7 remaining elements to vertex_grid1[][]
if (false == bBoundaryCrease1[0])
{
- vertex_grid1[3][0] = fsh.AllocateVertex(m_edge_grid[q0fvi][1]);
+ // When the level 0 vertex is valence 2 and the neignboring faces are triangles,
+ // this vertex needs to be added to the hash table
+ vertex_grid1[3][0] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[q0fvi][1]);
if ( nullptr == vertex_grid1[3][0])
return ON_SUBD_RETURN_ERROR(false);
}
@@ -2116,7 +2122,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if ( nullptr == vertex_grid1[3][1])
return ON_SUBD_RETURN_ERROR(false);
- vertex_grid1[3][2] = fsh.AllocateVertex(qf0_edges[1]);
+ vertex_grid1[3][2] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[1]);
if ( nullptr == vertex_grid1[3][2])
return ON_SUBD_RETURN_ERROR(false);
@@ -2124,7 +2130,7 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if ( nullptr == vertex_grid1[3][3])
return ON_SUBD_RETURN_ERROR(false);
- vertex_grid1[2][3] = fsh.AllocateVertex(qf0_edges[2]);
+ vertex_grid1[2][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, qf0_edges[2]);
if ( nullptr == vertex_grid1[2][3])
return ON_SUBD_RETURN_ERROR(false);
@@ -2134,14 +2140,15 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if (false == bBoundaryCrease1[3])
{
- vertex_grid1[0][3] = fsh.AllocateVertex(m_edge_grid[(q0fvi+3)%4][0]);
+ vertex_grid1[0][3] = fsh.AllocateEdgeSubdivisionVertex(bUseFindOrAllocate, m_edge_grid[(q0fvi + 3) % 4][0]);
if ( nullptr == vertex_grid1[0][3])
return ON_SUBD_RETURN_ERROR(false);
}
edge_grid1[1][0] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[2][1],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[0], qf0_vertices[1])
);
@@ -2149,8 +2156,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[1][1] = fsh.AllocateEdge(
- vertex_grid1[2][2],
- ON_SubDSectorType::IgnoredSectorWeight,
+ bUseFindOrAllocate,
+ vertex_grid1[2][2],
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][2],
at_crease_weight // ignored unless vertex_grid1[3][2] is tagged as a crease
);
@@ -2158,8 +2166,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[2][0] = fsh.AllocateEdge(
- vertex_grid1[2][2],
- ON_SubDSectorType::IgnoredSectorWeight,
+ bUseFindOrAllocate,
+ vertex_grid1[2][2],
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[2][3],
at_crease_weight // ignored unless vertex_grid1[2][3] is tagged as a crease
);
@@ -2167,8 +2176,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
edge_grid1[2][1] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[1][2],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[3], qf0_vertices[3])
);
@@ -2188,8 +2198,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[0] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[2][0],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][0],
at_crease_weight // ignored unless vertex_grid1[3][0] is tagged as a crease
);
@@ -2197,8 +2208,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
// m_edge_grid[q0fvi][1]
fedges[1] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[3][0],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[q0fvi][1], qf0_vertices[1])
);
@@ -2225,10 +2237,11 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[3][1],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[1]),
vertex_grid1[3][2],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if (fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -2255,8 +2268,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[3][2],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[3][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[1], qf0_vertices[2])
);
@@ -2264,10 +2278,11 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[3][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[2]),
vertex_grid1[2][3],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if (fedges[2].IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -2293,8 +2308,9 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[2][3],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(qf0_edges[2], qf0_vertices[3])
);
@@ -2322,19 +2338,21 @@ bool ON_SubDQuadNeighborhood::Subdivide(
return ON_SUBD_RETURN_ERROR(false);
fedges[1] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[1][3],
ON_SubDQuadFaceTopology_CopySectorWeight(m_edge_grid[(q0fvi+3)%4][0], qf0_vertices[3]),
vertex_grid1[0][3],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( fedges[1].IsNull())
return ON_SUBD_RETURN_ERROR(false);
fedges[2] = fsh.AllocateEdge(
+ bUseFindOrAllocate,
vertex_grid1[0][3],
at_crease_weight, // ingored unless vertex_grid1[0][3] is tagged as a crease
vertex_grid1[0][2],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( fedges[2].IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -2396,18 +2414,18 @@ bool ON_SubDQuadNeighborhood::Subdivide(
if (
qv0->SurfacePointIsSet()
&& qv0->GetSurfacePoint(qv0_sector_face,limit_point)
- && limit_point.IsSet(false)
+ && limit_point.IsSet(true)
)
{
limit_point.m_sector_face = qv1_sector_face; // qv1's sector face
limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face
- qv1->SetSavedSurfacePoint(false,limit_point);
+ qv1->SetSavedSurfacePoint(true,limit_point);
}
else if (qv1->GetSurfacePoint( qv1_sector_face, limit_point))
{
limit_point.m_sector_face = qv0_sector_face;
limit_point.m_next_sector_limit_point = nullptr; // sector checks are required to get "first" face
- qv0->SetSavedSurfacePoint(false,limit_point);
+ qv0->SetSavedSurfacePoint(true,limit_point);
}
}
@@ -2629,7 +2647,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
// of a creased original edge, this is the value to assign to the new
// edge's m_vertex_weight. The "2" is there because there would be 2
// sector faces if the subdivision was complete.
- const double at_crease2_weight = ON_SubDSectorType::CreaseSectorWeight(2);
+ const double at_crease2_weight = ON_SubDSectorType::CreaseSectorCoefficient(2);
const unsigned int N = face->m_edge_count;
@@ -2707,7 +2725,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
edge1 = m_fsh.AllocateEdge(
center_vertex1,
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
vertex1,
at_crease2_weight // ingored unless vertex1 is tagged as a crease
);
@@ -2766,7 +2784,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
edge1 = m_fsh.AllocateEdge(
ring_vertex1[0],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
ring_vertex1[1],
ON_SubDQuadFaceTopology_CopySectorWeight(prev_edge0, vertex0)
);
@@ -2779,7 +2797,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
ring_vertex1[1],
ON_SubDQuadFaceTopology_CopySectorWeight(edge0, vertex0),
ring_vertex1[2],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
@@ -2872,7 +2890,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
ring_vertex1[2],
at_crease2_weight, // ingored unless ring_vertex1[0] is tagged as a crease
vertex1,
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
if ( edge1.IsNull())
return ON_SUBD_RETURN_ERROR(false);
@@ -3061,7 +3079,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[0] = edge1;
- edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight);
+ edge1 = m_fsh.FindOrAllocateEdge(face1_corners[1], at_crease2_weight, face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[1] = edge1;
@@ -3110,7 +3128,7 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
face1_eptrs[0] = ON_SubDEdgePtr::Create(edge1_quartet[1], 1);
face1_eptrs[1] = ON_SubDEdgePtr::Create(edge1_quartet[0], 0);
- edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorWeight, face1_corners[3], at_crease2_weight);
+ edge1 = m_fsh.FindOrAllocateEdge(face1_corners[2], ON_SubDSectorType::IgnoredSectorCoefficient, face1_corners[3], at_crease2_weight);
if ( edge1.IsNull() )
return ON_SUBD_RETURN_ERROR(false);
face1_eptrs[2] = edge1;
@@ -3322,13 +3340,13 @@ bool ON_SubDFaceNeighborhood::QuadSubdivideHelper(
face1_corners[1],
at_crease2_weight,
face1_corners[2],
- ON_SubDSectorType::IgnoredSectorWeight
+ ON_SubDSectorType::IgnoredSectorCoefficient
);
break;
case 2:
edge1 = m_fsh.FindOrAllocateEdge(
face1_corners[2],
- ON_SubDSectorType::IgnoredSectorWeight,
+ ON_SubDSectorType::IgnoredSectorCoefficient,
face1_corners[3],
at_crease2_weight
);
diff --git a/opennurbs_subd_matrix.cpp b/opennurbs_subd_matrix.cpp
index 56bad4df..9677bc3e 100644
--- a/opennurbs_subd_matrix.cpp
+++ b/opennurbs_subd_matrix.cpp
@@ -971,7 +971,7 @@ unsigned int ON_SubDSectorType::GetSubdivisionMatrix(
if (N < 2 || R < 4)
return ON_SUBD_RETURN_ERROR(0);
- const double w = this->SectorWeight();
+ const double w = this->SectorCoefficient();
double* x;
double* y;
diff --git a/opennurbs_subd_mesh.cpp b/opennurbs_subd_mesh.cpp
index 506f0622..464836b1 100644
--- a/opennurbs_subd_mesh.cpp
+++ b/opennurbs_subd_mesh.cpp
@@ -1736,5 +1736,16 @@ void ON_SubD::ClearEvaluationCache() const
}
}
+void ON_SubD::ClearNeighborhoodEvaluationCache(const ON_SubDVertex * vertex, bool bTagChanged) const
+{
+ const ON_SubDLevel* level = ActiveLevelConstPointer();
+
+ if (nullptr != level)
+ {
+ const_cast(this)->ChangeContentSerialNumberForExperts();
+ level->ClearNeighborhoodEvaluationCache(vertex, bTagChanged);
+ }
+}
+
////////////////////////////////////////////////////////////////////////////
diff --git a/opennurbs_subd_ring.cpp b/opennurbs_subd_ring.cpp
index 789e2330..6b1af75a 100644
--- a/opennurbs_subd_ring.cpp
+++ b/opennurbs_subd_ring.cpp
@@ -467,7 +467,7 @@ static double Subdivide_CenterVertexSectorWeight(
)
{
if ( ON_SubD::EdgeTag::Crease == edge0->m_edge_tag)
- return ON_SubDSectorType::IgnoredSectorWeight;
+ return ON_SubDSectorType::IgnoredSectorCoefficient;
if (ON_SubD::EdgeTag::Smooth == edge0->m_edge_tag || ON_SubD::EdgeTag::SmoothX == edge0->m_edge_tag)
{
if (vertex0 == edge0->m_vertex[0])
@@ -570,9 +570,9 @@ const ON_SubDVertex* ON_SubD::SubdivideSector(
ON_SubD::EdgeTag edge1_tag = (ON_SubD::EdgeTag::SmoothX == edge0_tag) ? ON_SubD::EdgeTag::Smooth : edge0_tag;
const double at_crease_weight
= ON_SubD::EdgeTag::Crease == edge1_tag
- ? ON_SubDSectorType::CreaseSectorWeight(5-K)
- : ON_SubDSectorType::IgnoredSectorWeight;
- ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorWeight );
+ ? ON_SubDSectorType::CreaseSectorCoefficient(5-K)
+ : ON_SubDSectorType::IgnoredSectorCoefficient;
+ ON_SubDEdgePtr edge1 = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0,vertex0), vertex1, ON_SubDSectorType::IgnoredSectorCoefficient );
if (edge1.IsNull())
return ON_SUBD_RETURN_ERROR(nullptr);
edge1.Edge()->m_edge_tag = edge1_tag;
@@ -607,7 +607,7 @@ const ON_SubDVertex* ON_SubD::SubdivideSector(
v1[K] = fsh.AllocateVertex(edge0);
if (nullptr == v1[K])
return ON_SUBD_RETURN_ERROR(nullptr);
- e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorWeight);
+ e1[K] = fsh.AllocateEdge(v1[0], Subdivide_CenterVertexSectorWeight(edge0, vertex0), v1[K], ON_SubDSectorType::IgnoredSectorCoefficient);
if (e1[K].IsNull())
return ON_SUBD_RETURN_ERROR(nullptr);
e1[K].Edge()->m_edge_tag = edge1_tag;
@@ -617,8 +617,8 @@ const ON_SubDVertex* ON_SubD::SubdivideSector(
// quads
v1[2] = fsh.AllocateSectorFaceVertex(face0 );
- e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorWeight);
- e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorWeight, v1[3], at_crease_weight);
+ e1[1] = fsh.AllocateEdge(v1[1], at_crease_weight, v1[2], ON_SubDSectorType::IgnoredSectorCoefficient);
+ e1[2] = fsh.AllocateEdge(v1[2], ON_SubDSectorType::IgnoredSectorCoefficient, v1[3], at_crease_weight);
f1epts[1] = e1[1];
f1epts[2] = e1[2];
if (nullptr == fsh.AllocateQuad(face0->m_zero_face_id, face0->m_id, f1epts) )
diff --git a/opennurbs_subd_sector.cpp b/opennurbs_subd_sector.cpp
index c5b61534..59dbdce8 100644
--- a/opennurbs_subd_sector.cpp
+++ b/opennurbs_subd_sector.cpp
@@ -91,9 +91,9 @@ public:
-double ON_SubDSectorType::SectorWeightCalculationError()
+double ON_SubDSectorType::SectorCoefficientCalculationError()
{
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
}
bool ON_SubDSectorType::IsValid() const
@@ -112,28 +112,28 @@ bool ON_SubDSectorType::IsValid() const
case ON_SubD::VertexTag::Smooth:
if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle))
return ON_SUBD_RETURN_ERROR(false);
- if (!(m_sector_weight == ON_SubDSectorType::IgnoredSectorWeight))
+ if (!(m_sector_coefficient == ON_SubDSectorType::IgnoredSectorCoefficient))
return ON_SUBD_RETURN_ERROR(false);
break;
case ON_SubD::VertexTag::Crease:
if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle))
return ON_SUBD_RETURN_ERROR(false);
- if (!(m_sector_weight == ON_SubDSectorType::CreaseSectorWeight(m_sector_face_count)))
+ if (!(m_sector_coefficient == ON_SubDSectorType::CreaseSectorCoefficient(m_sector_face_count)))
return ON_SUBD_RETURN_ERROR(false);
break;
case ON_SubD::VertexTag::Corner:
if (!(m_corner_sector_angle_radians > 0.0 && m_corner_sector_angle_radians < ON_2PI))
return ON_SUBD_RETURN_ERROR(false);
- if (!(m_sector_weight == ON_SubDSectorType::CornerSectorWeight(m_sector_face_count,m_corner_sector_angle_radians)))
+ if (!(m_sector_coefficient == ON_SubDSectorType::CornerSectorCoefficient(m_sector_face_count,m_corner_sector_angle_radians)))
return ON_SUBD_RETURN_ERROR(false);
break;
case ON_SubD::VertexTag::Dart:
if (!(m_corner_sector_angle_radians == ON_SubDSectorType::IgnoredCornerSectorAngle))
return ON_SUBD_RETURN_ERROR(false);
- if (!(m_sector_weight == ON_SubDSectorType::DartSectorWeight(m_sector_face_count)))
+ if (!(m_sector_coefficient == ON_SubDSectorType::DartSectorCoefficient(m_sector_face_count)))
return ON_SUBD_RETURN_ERROR(false);
break;
@@ -492,12 +492,12 @@ double ON_SubDSectorType::CornerSectorAngleRadiansFromEdges(
-double ON_SubDSectorType::SectorWeightFromTheta(
+double ON_SubDSectorType::SectorCoefficientFromTheta(
double sector_theta
)
{
if (!(sector_theta > 0.0 && sector_theta <= ON_PI))
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
double cos_theta = cos(sector_theta);
@@ -533,20 +533,20 @@ double ON_SubDSectorType::SectorWeightFromTheta(
if (cos_theta <= -1.0)
return wrange[0];
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
}
-double ON_SubDSectorType::SectorWeight() const
+double ON_SubDSectorType::SectorCoefficient() const
{
- return m_sector_weight;
+ return m_sector_coefficient;
}
-bool ON_SubDSectorType::IsValidSectorWeightValue(
+bool ON_SubDSectorType::IsValidSectorCoefficientValue(
double weight_value,
- bool bAllowUnsetTaggedEndWeight
+ bool bAllowUnsetTaggedEndCoefficient
)
{
- return ((weight_value >= 0.0 && weight_value < 1.0) || (bAllowUnsetTaggedEndWeight && ON_SubDSectorType::UnsetSectorWeight == weight_value));
+ return ((weight_value >= 0.0 && weight_value < 1.0) || (bAllowUnsetTaggedEndCoefficient && ON_SubDSectorType::UnsetSectorCoefficient == weight_value));
}
bool ON_SubDSectorType::IsValidCornerSectorAngleRadians(
@@ -575,32 +575,32 @@ double ON_SubDSectorType::ClampCornerSectorAngleRadians(
}
-double ON_SubDSectorType::SmoothSectorWeight()
+double ON_SubDSectorType::SmoothSectorCoefficient()
{
- return ON_SubDSectorType::IgnoredSectorWeight;
+ return ON_SubDSectorType::IgnoredSectorCoefficient;
}
-double ON_SubDSectorType::CreaseSectorWeight(
+double ON_SubDSectorType::CreaseSectorCoefficient(
unsigned int sector_face_count
)
{
if (sector_face_count < 1)
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
double sector_theta = ON_SubDSectorType::CreaseSectorTheta(sector_face_count);
- return ON_SubDSectorType::SectorWeightFromTheta(sector_theta);
+ return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta);
}
-double ON_SubDSectorType::DartSectorWeight(
+double ON_SubDSectorType::DartSectorCoefficient(
unsigned int sector_face_count
)
{
if (sector_face_count < 2)
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
double sector_theta = ON_SubDSectorType::DartSectorTheta(sector_face_count);
- return ON_SubDSectorType::SectorWeightFromTheta(sector_theta);
+ return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta);
}
-double ON_SubDSectorType::CornerSectorWeight(
+double ON_SubDSectorType::CornerSectorCoefficient(
unsigned int sector_face_count,
double corner_sector_angle_radians
)
@@ -613,9 +613,9 @@ double ON_SubDSectorType::CornerSectorWeight(
{
const double sector_theta = ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count,corner_sector_angle_radians);
if (sector_theta >= 0.0)
- return ON_SubDSectorType::SectorWeightFromTheta(sector_theta);
+ return ON_SubDSectorType::SectorCoefficientFromTheta(sector_theta);
}
- return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorWeight);
+ return ON_SUBD_RETURN_ERROR(ON_SubDSectorType::ErrorSectorCoefficient);
}
static int CompareUnsinged(unsigned int a, unsigned int b)
@@ -719,10 +719,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateSmoothSectorType(
= ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)
? sector_face_count
: 0;
- st.m_sector_weight
+ st.m_sector_coefficient
= (st.m_sector_face_count>0)
- ? ON_SubDSectorType::IgnoredSectorWeight
- : ON_SubDSectorType::UnsetSectorWeight;
+ ? ON_SubDSectorType::IgnoredSectorCoefficient
+ : ON_SubDSectorType::UnsetSectorCoefficient;
st.m_sector_theta
= (st.m_sector_face_count>0)
? ON_SubDSectorType::SmoothSectorTheta
@@ -746,10 +746,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateCreaseSectorType(
= ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)
? sector_face_count
: 0;
- st.m_sector_weight
+ st.m_sector_coefficient
= (st.m_sector_face_count>0)
- ? ON_SubDSectorType::CreaseSectorWeight(sector_face_count)
- : ON_SubDSectorType::UnsetSectorWeight;
+ ? ON_SubDSectorType::CreaseSectorCoefficient(sector_face_count)
+ : ON_SubDSectorType::UnsetSectorCoefficient;
st.m_sector_theta
= (st.m_sector_face_count>0)
? ON_SubDSectorType::CreaseSectorTheta(sector_face_count)
@@ -774,10 +774,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateDartSectorType(
= ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)
? sector_face_count
: 0;
- st.m_sector_weight
+ st.m_sector_coefficient
= (st.m_sector_face_count>0)
- ? ON_SubDSectorType::DartSectorWeight(sector_face_count)
- : ON_SubDSectorType::UnsetSectorWeight;
+ ? ON_SubDSectorType::DartSectorCoefficient(sector_face_count)
+ : ON_SubDSectorType::UnsetSectorCoefficient;
st.m_sector_theta
= (st.m_sector_face_count>0)
? ON_SubDSectorType::DartSectorTheta(sector_face_count)
@@ -826,10 +826,10 @@ ON_SubDSectorType ON_SubDSectorType::CreateCornerSectorType(
= ON_SubDSectorType_IsValidFaceCount(vertex_tag,sector_face_count)
? sector_face_count
: 0;
- st.m_sector_weight
+ st.m_sector_coefficient
= (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians)
- ? ON_SubDSectorType::CornerSectorWeight( sector_face_count, corner_sector_angle_radians)
- : ON_SubDSectorType::UnsetSectorWeight;
+ ? ON_SubDSectorType::CornerSectorCoefficient( sector_face_count, corner_sector_angle_radians)
+ : ON_SubDSectorType::UnsetSectorCoefficient;
st.m_sector_theta
= (st.m_sector_face_count > 0 && ON_SubDSectorType::UnsetCornerSectorAngle != corner_sector_angle_radians)
? ON_SubDSectorType::CornerSectorThetaFromCornerAngle(sector_face_count, corner_sector_angle_radians)
diff --git a/opennurbs_subd_texture.cpp b/opennurbs_subd_texture.cpp
index bcf9234b..03d345fa 100644
--- a/opennurbs_subd_texture.cpp
+++ b/opennurbs_subd_texture.cpp
@@ -377,7 +377,8 @@ bool ON_SubD::SetTextureCoordinatesFromFaceDomains() const
{
if (ON_SubDTextureDomainType::Unset == this->TextureDomainType())
{
- if (false == SetTextureDomains(ON_SubDTextureDomainType::PerFace, false, false))
+ // uset default to packed
+ if (false == SetTextureDomains(ON_SubDTextureDomainType::Packed, false, false))
return false;
}
ON_SubDFaceIterator fit(*this);
diff --git a/opennurbs_symmetry.cpp b/opennurbs_symmetry.cpp
new file mode 100644
index 00000000..aad1e515
--- /dev/null
+++ b/opennurbs_symmetry.cpp
@@ -0,0 +1,1086 @@
+/* $NoKeywords: $ */
+/*
+//
+// Copyright (c) 1993-2019 Robert McNeel & Associates. All rights reserved.
+// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
+// McNeel & Associates.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
+// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
+// MERCHANTABILITY ARE HEREBY DISCLAIMED.
+//
+// For complete openNURBS copyright information see .
+//
+////////////////////////////////////////////////////////////////
+*/
+
+#include "opennurbs.h"
+
+#if !defined(ON_COMPILING_OPENNURBS)
+// This check is included in all opennurbs source .c and .cpp files to insure
+// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
+// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
+// and the opennurbs .h files alter what is declared and how it is declared.
+#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
+#endif
+
+ON_Symmetry::Type ON_Symmetry::SymmetryTypeFromUnsigned(unsigned int symmetry_type_as_unsigned)
+{
+ switch (symmetry_type_as_unsigned)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Unset);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Reflect);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Rotate);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::ReflectAndRotate);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Inversion);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Type::Cyclic);
+ }
+
+ ON_ERROR("Invalid type_as_unsigned parameter");
+ return ON_Symmetry::Type::Unset;
+}
+
+const ON_wString ON_Symmetry::SymmetryTypeToString(ON_Symmetry::Type symmetry_type)
+{
+ const wchar_t* s;
+ switch (symmetry_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ s = L"Unset";
+ break;
+ case ON_Symmetry::Type::Reflect:
+ s = L"Reflect";
+ break;
+ case ON_Symmetry::Type::Rotate:
+ s = L"Rotate";
+ break;
+ case ON_Symmetry::Type::ReflectAndRotate:
+ s = L"ReflectAndRotate";
+ break;
+ case ON_Symmetry::Type::Inversion:
+ s = L"Inversion";
+ break;
+ case ON_Symmetry::Type::Cyclic:
+ s = L"Cyclic";
+ break;
+ default:
+ s = nullptr;
+ break;
+ }
+ return ON_wString(s);
+}
+
+
+ON_Symmetry::Coordinates ON_Symmetry::SymmetryCoordinatesFromUnsigned(unsigned int symmetry_coordinates_as_unsigned)
+{
+ switch (symmetry_coordinates_as_unsigned)
+ {
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::Unset);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::Object);
+ ON_ENUM_FROM_UNSIGNED_CASE(ON_Symmetry::Coordinates::World);
+ }
+
+ ON_ERROR("Invalid symmetry_coordinates_as_unsigned parameter");
+ return ON_Symmetry::Coordinates::Unset;
+}
+
+const ON_wString ON_Symmetry::SymmetryCoordinatesToString(ON_Symmetry::Coordinates symmetry_coordinates)
+{
+ const wchar_t* s;
+ switch (symmetry_coordinates)
+ {
+ case ON_Symmetry::Coordinates::Unset:
+ s = L"Unset";
+ break;
+ case ON_Symmetry::Coordinates::Object:
+ s = L"Object";
+ break;
+ case ON_Symmetry::Coordinates::World:
+ s = L"World";
+ break;
+ default:
+ s = nullptr;
+ break;
+ }
+ return ON_wString(s);
+}
+
+bool ON_Symmetry::Write(ON_BinaryArchive& archive) const
+{
+ if (false == archive.BeginWrite3dmAnonymousChunk(2))
+ return false;
+
+ bool rc = false;
+ for (;;)
+ {
+ const ON_Symmetry::Type symmetry_type = IsSet() ? SymmetryType() : ON_Symmetry::Type::Unset;
+ const unsigned char utype = static_cast(symmetry_type);
+ if (false == archive.WriteChar(utype))
+ break;
+ if (ON_Symmetry::Type::Unset == symmetry_type)
+ {
+ rc = true;
+ break;
+ }
+ if (false == archive.WriteInt(m_inversion_order))
+ break;
+ if (false == archive.WriteInt(m_cyclic_order))
+ break;
+ if (false == archive.WriteUuid(m_id))
+ break;
+
+ if (archive.BeginWrite3dmAnonymousChunk(1))
+ {
+ switch (m_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ break;
+ case ON_Symmetry::Type::Reflect:
+ rc = archive.WritePlaneEquation(m_reflection_plane);
+ break;
+ case ON_Symmetry::Type::Rotate:
+ rc = archive.WriteLine(m_rotation_axis);
+ break;
+ case ON_Symmetry::Type::ReflectAndRotate:
+ rc = archive.WritePlaneEquation(m_reflection_plane) && archive.WriteLine(m_rotation_axis);
+ break;
+ case ON_Symmetry::Type::Inversion:
+ rc = archive.WriteXform(m_inversion_transform);
+ break;
+ case ON_Symmetry::Type::Cyclic:
+ rc = archive.WriteXform(m_cyclic_transform);
+ break;
+ default:
+ ON_ERROR("You added a new enum value but failed to update archive IO code.");
+ break;
+ }
+ if (false == archive.EndWrite3dmChunk())
+ rc = false;
+ }
+
+ // ON_Symmetry::Coordinates added Dec 16, 2019 chunk version 2
+ const ON_Symmetry::Coordinates symmetry_coordinates = IsSet() ? SymmetryCoordinates() : ON_Symmetry::Coordinates::Unset;
+ const unsigned char ucoordinates = static_cast(symmetry_coordinates);
+ if (false == archive.WriteChar(ucoordinates))
+ break;
+
+ break;
+ }
+ if (false == archive.EndWrite3dmChunk())
+ rc = false;
+ return rc;
+}
+
+
+bool ON_Symmetry::Read(ON_BinaryArchive& archive)
+{
+ *this = ON_Symmetry::Unset;
+
+ int chunk_version = 0;
+ if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version))
+ return false;
+
+ ON_Symmetry::Type symmetry_type = ON_Symmetry::Type::Unset;
+ unsigned int inversion_order = 0;
+ unsigned int cyclic_order = 0;
+ ON_UUID symmetry_id = ON_nil_uuid;
+ ON_Xform inversion_transform = ON_Xform::Nan;
+ ON_Xform cyclic_transform = ON_Xform::Nan;
+ ON_PlaneEquation reflection_plane = ON_PlaneEquation::NanPlaneEquation;
+ ON_Line rotation_axis = ON_Line::NanLine;
+
+ bool rc = false;
+ for (;;)
+ {
+ if (chunk_version <= 0)
+ break;
+ unsigned char utype = 0;
+ if (false == archive.ReadChar(&utype))
+ break;
+ symmetry_type = ON_Symmetry::SymmetryTypeFromUnsigned(utype);
+ if (ON_Symmetry::Type::Unset == symmetry_type)
+ {
+ rc = true;
+ break;
+ }
+
+
+ if (false == archive.ReadInt(&inversion_order))
+ break;
+ if (false == archive.ReadInt(&cyclic_order))
+ break;
+ if (false == archive.ReadUuid(symmetry_id))
+ break;
+
+ int inner_chunk_version = 0;
+ if (false == archive.BeginRead3dmAnonymousChunk(&inner_chunk_version))
+ break;
+
+ ON_Symmetry symmetry;
+
+ ON_Symmetry::Coordinates symmetry_coordinates = ON_Symmetry::Coordinates::Object;
+
+ switch (symmetry_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ break;
+
+ case ON_Symmetry::Type::Reflect:
+ rc = archive.ReadPlaneEquation(reflection_plane);
+ if (rc)
+ symmetry = ON_Symmetry::CreateReflectSymmetry(reflection_plane, symmetry_coordinates);
+ break;
+
+ case ON_Symmetry::Type::Rotate:
+ rc = archive.ReadLine(rotation_axis);
+ if (rc)
+ symmetry = ON_Symmetry::CreateRotateSymmetry(rotation_axis, cyclic_order, symmetry_coordinates);
+ break;
+
+ case ON_Symmetry::Type::ReflectAndRotate:
+ rc = archive.ReadPlaneEquation(reflection_plane) && archive.ReadLine(rotation_axis);
+ if (rc)
+ symmetry = ON_Symmetry::CreateReflectAndRotateSymmetry(reflection_plane, rotation_axis, cyclic_order, symmetry_coordinates);
+ break;
+
+ case ON_Symmetry::Type::Inversion:
+ rc = archive.ReadXform(inversion_transform);
+ if (rc)
+ symmetry = ON_Symmetry::CreateInversionSymmetry(symmetry_id, inversion_transform, symmetry_coordinates);
+ break;
+
+ case ON_Symmetry::Type::Cyclic:
+ rc = archive.ReadXform(cyclic_transform);
+ if (rc)
+ symmetry = ON_Symmetry::CreateCyclicSymmetry(symmetry_id, cyclic_transform, cyclic_order, symmetry_coordinates);
+ break;
+
+ default:
+ // Old code reading a file containing a future type.
+ symmetry_type = ON_Symmetry::Type::Unset;
+ rc = true; // means no media reading error
+ break;
+ }
+
+ if (
+ rc
+ && ON_Symmetry::Type::Unset != symmetry_type
+ && symmetry.SymmetryType() == symmetry_type
+ && symmetry.InversionOrder() == inversion_order
+ && symmetry.CyclicOrder() == cyclic_order
+ && symmetry.SymmetryId() == symmetry_id
+ )
+ {
+ *this = symmetry;
+ }
+
+ if (false == archive.EndRead3dmChunk())
+ rc = false;
+
+ if (chunk_version < 2)
+ break;
+
+ unsigned char ucoordinates = 0;
+ rc = archive.ReadChar(&ucoordinates);
+ if (false == rc)
+ break;
+ symmetry_coordinates = ON_Symmetry::SymmetryCoordinatesFromUnsigned(ucoordinates);
+ if (ON_Symmetry::Coordinates::Unset != symmetry_coordinates && m_coordinates != symmetry_coordinates)
+ m_coordinates = symmetry_coordinates;
+
+ break;
+ }
+
+ if (false == archive.EndRead3dmChunk())
+ rc = false;
+
+ return rc;
+}
+
+void ON_PlaneEquation::Dump(class ON_TextLog& text_log) const
+{
+ // print -0 as 0.
+ double c[4] = { (0.0==x) ? 0.0 : x,(0.0 == y) ? 0.0 : y,(0.0 == z) ? 0.0 : z,(0.0 == d) ? 0.0 : d };
+ for (int i = 0; i < 3; ++i)
+ {
+ if (false == (0.0 != c[i] && 0.0 == c[(i + 1) % 3] && 0.0 == c[(i + 2) % 3]) )
+ continue;
+ const char* coord = (0 == i) ? "x" : ((1 == i) ? "y" : "z");
+ if (0.0 == c[3])
+ text_log.Print(L"%s = 0", coord);
+ else if (1.0 == c[i])
+ text_log.Print(L"%s = %g", coord, -c[3]);
+ else
+ text_log.Print(L"%g*%s = %g", c[i] , coord, -c[3]);
+ return;
+ }
+
+ // general case
+ text_log.Print(L"%g*x + %g*y + %g*z + %g = 0", c[0], c[1], c[2], c[3]);
+}
+
+void ON_Symmetry::Dump(ON_TextLog& text_log) const
+{
+ const ON_wString type = ON_Symmetry::SymmetryTypeToString(m_type);
+ const ON_wString coordinates = ON_Symmetry::SymmetryCoordinatesToString(m_coordinates);
+ text_log.Print(L"%ls %ls symmetry\n",static_cast(type), static_cast(coordinates));
+ if (IsUnset())
+ return;
+
+ text_log.Print(L"Motif count: %u\n", MotifCount());
+
+ switch (m_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ break;
+
+ case ON_Symmetry::Type::Reflect:
+ {
+ const ON_PlaneEquation e = ReflectionPlane();
+ text_log.Print(L"plane: ");
+ ReflectionPlane().Dump(text_log);
+ text_log.PrintNewLine();
+ }
+ break;
+
+ case ON_Symmetry::Type::Rotate:
+ {
+ text_log.Print(L"rotation count: %u (%g degrees)\n", RotationCount(), RotationAngleDegrees());
+ const ON_Line axis = RotationAxis();
+ text_log.Print(L"axis: ");
+ text_log.Print(axis.from);
+ text_log.Print(L", ");
+ text_log.Print(axis.to);
+ text_log.PrintNewLine();
+ }
+ break;
+
+ case ON_Symmetry::Type::ReflectAndRotate:
+ {
+ const ON_PlaneEquation e = ReflectionPlane();
+ text_log.Print(L"plane: ");
+ ReflectionPlane().Dump(text_log);
+ text_log.PrintNewLine();
+ text_log.Print(L"rotation count: %u (%g degrees)\n", RotationCount(), RotationAngleDegrees());
+ const ON_Line axis = RotationAxis();
+ text_log.Print(L"axis: ");
+ text_log.Print(axis.from);
+ text_log.Print(L", ");
+ text_log.Print(axis.to);
+ text_log.PrintNewLine();
+ }
+ break;
+
+ case ON_Symmetry::Type::Inversion:
+ {
+ const ON_Line line = RotationAxis();
+ text_log.Print(InversionTransform());
+ text_log.PrintNewLine();
+ }
+ break;
+
+ case ON_Symmetry::Type::Cyclic:
+ {
+ const ON_Line line = RotationAxis();
+ text_log.Print(CyclicTransform());
+ text_log.PrintNewLine();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+const ON_Symmetry ON_Symmetry::TransformConditionally(const ON_Xform& xform) const
+{
+ return
+ (ON_Symmetry::Coordinates::Object == SymmetryCoordinates())
+ ? ON_Symmetry::TransformUnconditionally(xform)
+ : ON_Symmetry(*this);
+}
+
+const ON_Symmetry ON_Symmetry::TransformUnconditionally(const ON_Xform& xform) const
+{
+ switch (m_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ break;
+
+ case ON_Symmetry::Type::Reflect:
+ {
+ if (false == m_reflection_plane.IsValid())
+ break;
+ ON_PlaneEquation e = m_reflection_plane;
+ e.Transform(xform);
+ if (false == e.IsValid())
+ break;
+ return ON_Symmetry::CreateReflectSymmetry(e, m_coordinates);
+ }
+ break;
+
+ case ON_Symmetry::Type::Rotate:
+ {
+ if (false == m_rotation_axis.IsValid())
+ break;
+ ON_Line a = m_rotation_axis;
+ a.Transform(xform);
+ if (false == a.IsValid())
+ break;
+ return ON_Symmetry::CreateRotateSymmetry(a, RotationCount(), m_coordinates);
+ }
+ break;
+
+ case ON_Symmetry::Type::ReflectAndRotate:
+ {
+ if (false == m_reflection_plane.IsValid())
+ break;
+ if (false == m_rotation_axis.IsValid())
+ break;
+ ON_PlaneEquation e = m_reflection_plane;
+ e.Transform(xform);
+ if (false == e.IsValid())
+ break;
+ ON_Line a = m_rotation_axis;
+ a.Transform(xform);
+ if (false == a.IsValid())
+ break;
+ return ON_Symmetry::CreateReflectAndRotateSymmetry(e, a, RotationCount(), m_coordinates);
+ }
+ break;
+
+ case ON_Symmetry::Type::Inversion:
+ {
+ const ON_Xform xform_inverse = xform.Inverse();
+ const ON_Xform inversion_xform = xform * InversionTransform()*xform_inverse;
+ return ON_Symmetry::CreateInversionSymmetry(SymmetryId(), inversion_xform, m_coordinates);
+ }
+ break;
+
+ case ON_Symmetry::Type::Cyclic:
+ {
+ const ON_Xform xform_inverse = xform.Inverse();
+ const ON_Xform cyclic_xform = xform * CyclicTransform()*xform_inverse;
+ return ON_Symmetry::CreateCyclicSymmetry(SymmetryId(), cyclic_xform, CyclicOrder(), m_coordinates);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ON_Symmetry::Unset;
+}
+
+const ON_Symmetry ON_Symmetry::CreateInversionSymmetry(
+ ON_UUID symmetry_id,
+ ON_Xform inversion_transform,
+ ON_Symmetry::Coordinates symmetry_coordinates
+)
+{
+ for (;;)
+ {
+ if (false == inversion_transform.IsValid())
+ break;
+
+ const double det = inversion_transform.Determinant();
+ if (false == (det < 0.0))
+ break;
+
+ ON_Xform x = inversion_transform* inversion_transform;
+ if (false == x.IsIdentity(ON_Symmetry::ZeroTolerance))
+ break;
+
+ if (false == (ON_nil_uuid == symmetry_id) )
+ {
+ // prohibit using built-in ids
+ if (ON_Symmetry::ReflectId == symmetry_id)
+ break;
+ if (ON_Symmetry::RotateId == symmetry_id)
+ break;
+ if (ON_Symmetry::ReflectAndRotateId == symmetry_id)
+ break;
+ }
+
+ ON_Symmetry symmetry;
+ symmetry.m_type = ON_Symmetry::Type::Cyclic;
+ symmetry.m_coordinates = symmetry_coordinates;
+ symmetry.m_inversion_order = 2;
+ symmetry.m_cyclic_order = 1;
+ symmetry.m_id = symmetry_id;
+ symmetry.m_inversion_transform = inversion_transform;
+ symmetry.m_cyclic_transform = ON_Xform::IdentityTransformation;
+ return symmetry;
+ }
+
+ return ON_Symmetry::Unset;
+}
+
+
+const ON_Symmetry ON_Symmetry::CreateCyclicSymmetry(
+ ON_UUID symmetry_id,
+ ON_Xform cyclic_transform,
+ unsigned int cyclic_order,
+ ON_Symmetry::Coordinates symmetry_coordinates
+)
+{
+ for (;;)
+ {
+ if (cyclic_order < 2)
+ break;
+ if (cyclic_order > ON_Symmetry::MaximumOrder)
+ break;
+ if (false == cyclic_transform.IsValid())
+ break;
+
+ const double det = cyclic_transform.Determinant();
+ if (2 == cyclic_order || 1 == (cyclic_order % 2))
+ {
+ if (false == (det > 0.0))
+ break;
+ }
+ else
+ {
+ if (false == (det != 0.0))
+ break;
+ }
+
+ unsigned n = 1;
+ ON_Xform x = cyclic_transform;
+ while (n < cyclic_order && x.IsValid() && false == x.IsIdentity(ON_Symmetry::ZeroTolerance))
+ {
+ x = cyclic_transform * x;
+ ++n;
+ }
+ if (n != cyclic_order)
+ break;
+ if (false == x.IsIdentity(ON_Symmetry::ZeroTolerance))
+ break;
+
+ if (false == (ON_nil_uuid == symmetry_id))
+ {
+ // prohibit using built-in ids
+ if (ON_Symmetry::ReflectId == symmetry_id)
+ break;
+ if (ON_Symmetry::RotateId == symmetry_id)
+ break;
+ if (ON_Symmetry::ReflectAndRotateId == symmetry_id)
+ break;
+ }
+
+ ON_Symmetry symmetry;
+ symmetry.m_type = ON_Symmetry::Type::Cyclic;
+ symmetry.m_coordinates = symmetry_coordinates;
+ symmetry.m_inversion_order = 1;
+ symmetry.m_cyclic_order = cyclic_order;
+ symmetry.m_id = symmetry_id;
+ symmetry.m_inversion_transform = ON_Xform::IdentityTransformation;
+ symmetry.m_cyclic_transform = cyclic_transform;
+ return symmetry;
+ }
+
+ return ON_Symmetry::Unset;
+}
+
+const ON_Symmetry ON_Symmetry::CreateReflectSymmetry(
+ ON_PlaneEquation reflection_plane,
+ ON_Symmetry::Coordinates symmetry_coordinates
+)
+{
+ for (;;)
+ {
+ if (false == reflection_plane.IsValid())
+ break;
+ const ON_Xform xform(ON_Xform::MirrorTransformation(reflection_plane));
+ ON_Symmetry symmetry = ON_Symmetry::CreateInversionSymmetry(ON_nil_uuid, xform, symmetry_coordinates);
+ if (ON_Symmetry::Type::Cyclic != symmetry.m_type)
+ break;
+ symmetry.m_type = ON_Symmetry::Type::Reflect;
+ symmetry.m_coordinates = symmetry_coordinates;
+ symmetry.m_id = ON_Symmetry::ReflectId;
+ symmetry.m_reflection_plane = reflection_plane;
+ return symmetry;
+ }
+ return ON_Symmetry::Unset;
+}
+
+const ON_Symmetry ON_Symmetry::CreateRotateSymmetry(
+ ON_Line rotation_axis,
+ unsigned int rotation_count,
+ ON_Symmetry::Coordinates symmetry_coordinates
+)
+{
+ for (;;)
+ {
+ if (rotation_count < 2 || rotation_count > ON_Symmetry::MaximumOrder)
+ break;
+ if (false == rotation_axis.IsValid())
+ break;
+ const ON_Xform R = Internal_RotationXform(rotation_axis, 1, rotation_count);
+ ON_Symmetry symmetry = ON_Symmetry::CreateCyclicSymmetry(ON_nil_uuid, R, rotation_count, symmetry_coordinates);
+ if (ON_Symmetry::Type::Cyclic != symmetry.m_type)
+ break;
+ symmetry.m_type = ON_Symmetry::Type::Rotate;
+ symmetry.m_coordinates = symmetry_coordinates;
+ symmetry.m_id = ON_Symmetry::RotateId;
+ symmetry.m_rotation_axis = rotation_axis;
+ return symmetry;
+ }
+ return ON_Symmetry::Unset;
+}
+
+
+const ON_Symmetry ON_Symmetry::CreateReflectAndRotateSymmetry(
+ ON_PlaneEquation reflection_plane,
+ ON_Line rotation_axis,
+ unsigned int rotation_count,
+ ON_Symmetry::Coordinates symmetry_coordinates
+)
+{
+ for (;;)
+ {
+ if (false == reflection_plane.IsValid())
+ break;
+ if (false == rotation_axis.IsValid())
+ break;
+
+ // rotation axis must be in the reflection plane
+ const double h0 = reflection_plane.ValueAt(rotation_axis.from);
+ const double h1 = reflection_plane.ValueAt(rotation_axis.to);
+ if (false == (fabs(h0) <= ON_ZERO_TOLERANCE))
+ break;
+ if (false == (fabs(h1) <= ON_ZERO_TOLERANCE))
+ break;
+
+ const ON_Symmetry reflection = CreateReflectSymmetry(reflection_plane, symmetry_coordinates);
+ if (ON_Symmetry::Type::Reflect != reflection.SymmetryType())
+ break;
+ const ON_Symmetry rotation = CreateRotateSymmetry(rotation_axis,rotation_count, symmetry_coordinates);
+ if (ON_Symmetry::Type::Rotate != rotation.SymmetryType())
+ break;
+
+ ON_Symmetry symmetry;
+ symmetry.m_type = ON_Symmetry::Type::ReflectAndRotate;
+ symmetry.m_coordinates = symmetry_coordinates;
+ symmetry.m_inversion_order = reflection.m_inversion_order;
+ symmetry.m_cyclic_order = rotation.m_cyclic_order;
+ symmetry.m_id = ON_Symmetry::ReflectAndRotateId;
+ symmetry.m_inversion_transform = reflection.m_inversion_transform;
+ symmetry.m_cyclic_transform = rotation.m_cyclic_transform;
+ symmetry.m_reflection_plane = reflection.m_reflection_plane;
+ symmetry.m_rotation_axis = rotation.m_rotation_axis;
+ return symmetry;
+ }
+ return ON_Symmetry::Unset;
+}
+
+int ON_Symmetry::Internal_CompareDouble(const double* lhs, const double* rhs, size_t count)
+{
+ if (lhs == rhs)
+ return 0;
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+ for (size_t i = 0; i < count; ++i)
+ {
+ const double x = lhs[i];
+ const double y = rhs[i];
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+ const bool xok = (x == x) ? true : false;
+ const bool yok = (y == y) ? true : false;
+ if (xok == yok)
+ continue;
+ if (false == xok)
+ return 1; // lhs is a nan
+ if (false == yok)
+ return -1; // rhs is a nan
+ }
+ return 0;
+}
+
+int ON_Symmetry::Compare(const ON_Symmetry* lhs, const ON_Symmetry* rhs)
+{
+ if (lhs == rhs)
+ return 0;
+
+ // sort nulls to end
+ if (nullptr == lhs)
+ return 1;
+ if (nullptr == rhs)
+ return -1;
+
+ if (static_cast(lhs->m_type) < static_cast(rhs->m_type))
+ return -1;
+ if (static_cast(lhs->m_type) > static_cast(rhs->m_type))
+ return 1;
+ if (ON_Symmetry::Type::Unset == lhs->m_type)
+ return 0;
+
+ if (static_cast(lhs->m_coordinates) < static_cast(rhs->m_coordinates))
+ return -1;
+ if (static_cast(lhs->m_coordinates) > static_cast(rhs->m_coordinates))
+ return 1;
+
+
+ if (lhs->m_inversion_order < rhs->m_inversion_order)
+ return -1;
+ if (lhs->m_inversion_order > rhs->m_inversion_order)
+ return 1;
+
+ if (lhs->m_cyclic_order < rhs->m_cyclic_order)
+ return -1;
+ if (lhs->m_cyclic_order > rhs->m_cyclic_order)
+ return 1;
+
+ if (0U == lhs->m_inversion_order || 0U == lhs->m_cyclic_order)
+ return 0;
+
+ if (ON_Symmetry::Type::Reflect == lhs->m_type || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type )
+ {
+ const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_reflection_plane.x, &rhs->m_reflection_plane.x, 4);
+ if (0 != rc)
+ return rc;
+ }
+
+ if (ON_Symmetry::Type::Rotate == lhs->m_type || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type)
+ {
+ const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_rotation_axis.from.x, &rhs->m_rotation_axis.from.x, 6);
+ if (0 != rc)
+ return rc;
+ }
+
+ if (
+ ON_Symmetry::Type::Reflect == lhs->m_type
+ || ON_Symmetry::Type::Rotate == lhs->m_type
+ || ON_Symmetry::Type::ReflectAndRotate == lhs->m_type
+ )
+ return 0;
+
+ if (lhs->m_inversion_order > 1)
+ {
+ const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_inversion_transform.m_xform[0][0], &rhs->m_inversion_transform.m_xform[0][0], 16);
+ if (0 != rc)
+ return rc;
+ }
+
+ if (lhs->m_cyclic_order > 1)
+ {
+ const int rc = ON_Symmetry::Internal_CompareDouble(&lhs->m_inversion_transform.m_xform[0][0], &rhs->m_inversion_transform.m_xform[0][0], 16);
+ if (0 != rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+ON_Symmetry::Type ON_Symmetry::SymmetryType() const
+{
+ return m_type;
+}
+
+ON_Symmetry::Coordinates ON_Symmetry::SymmetryCoordinates() const
+{
+ return m_coordinates;
+}
+
+const ON_UUID ON_Symmetry::SymmetryId() const
+{
+ return m_id;
+}
+
+void ON_Symmetry::Clear()
+{
+ *this = ON_Symmetry::Unset;
+}
+
+bool ON_Symmetry::IsSet() const
+{
+ return
+ ON_Symmetry::Type::Unset != m_type
+ && (1 == m_inversion_order || 2 == m_inversion_order)
+ && m_cyclic_order >= 1
+ && MotifCount() >= 2
+ ;
+}
+
+bool ON_Symmetry::IsUnset() const
+{
+ return (false == IsSet());
+}
+
+unsigned int ON_Symmetry::MotifCount() const
+{
+ return InversionOrder()*CyclicOrder();
+}
+
+
+unsigned int ON_Symmetry::InversionOrder() const
+{
+ return m_inversion_order;
+}
+
+
+unsigned int ON_Symmetry::CyclicOrder() const
+{
+ return m_cyclic_order;
+}
+
+const ON_Xform ON_Symmetry::InversionTransform() const
+{
+ return IsSet() ? m_inversion_transform : ON_Xform::Nan;
+}
+
+const ON_Xform ON_Symmetry::CyclicTransform() const
+{
+ return IsSet() ? m_cyclic_transform : ON_Xform::Nan;
+}
+
+const ON_SHA1_Hash ON_Symmetry::Hash() const
+{
+ for(;;)
+ {
+ if (false == IsSet())
+ break;
+
+ ON_SHA1 sha1;
+
+ const unsigned char t = static_cast(m_type);
+ sha1.AccumulateBytes(&t, sizeof(t));
+
+ const unsigned char c = static_cast(m_coordinates);
+ sha1.AccumulateBytes(&c, sizeof(c));
+
+ sha1.AccumulateInteger32(InversionOrder());
+ sha1.AccumulateInteger32(CyclicOrder());
+
+ if (ON_Symmetry::Type::Reflect == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ sha1.AccumulateDoubleArray(4, &m_reflection_plane.x);
+
+ if (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ sha1.AccumulateDoubleArray(6, &m_rotation_axis.from.x);
+
+ if (ON_Symmetry::Type::Reflect != m_type && ON_Symmetry::Type::Rotate != m_type && ON_Symmetry::Type::ReflectAndRotate != m_type)
+ {
+ if (InversionOrder() > 1)
+ sha1.AccumulateDoubleArray(16, &m_inversion_transform.m_xform[0][0]);
+ if (CyclicOrder() > 1)
+ sha1.AccumulateDoubleArray(16, &m_cyclic_transform.m_xform[0][0]);
+ }
+ return sha1.Hash();
+ }
+
+ return ON_SHA1_Hash::EmptyContentHash;
+}
+
+const ON_PlaneEquation ON_Symmetry::ReflectionPlane() const
+{
+ return (ON_Symmetry::Type::Reflect == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_reflection_plane
+ : ON_PlaneEquation::NanPlaneEquation;
+}
+
+const ON_Line ON_Symmetry::RotationAxis() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_rotation_axis
+ : ON_Line::NanLine;
+}
+
+const ON_3dPoint ON_Symmetry::RotationAxisPoint() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_rotation_axis.from
+ : ON_3dPoint::NanPoint;
+}
+
+const ON_3dVector ON_Symmetry::RotationAxisDirection() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_rotation_axis.Direction()
+ : ON_3dVector::NanVector;
+}
+
+const ON_3dVector ON_Symmetry::RotationAxisTangent() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_rotation_axis.Tangent()
+ : ON_3dVector::NanVector;
+}
+
+unsigned int ON_Symmetry::RotationCount() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? m_cyclic_order
+ : 0U;
+}
+
+double ON_Symmetry::RotationAngleDegrees() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? (360.0 / ((double)RotationCount()))
+ : ON_DBL_QNAN;
+}
+
+double ON_Symmetry::RotationAngleRadians() const
+{
+ return (ON_Symmetry::Type::Rotate == m_type || ON_Symmetry::Type::ReflectAndRotate == m_type)
+ ? ((2.0*ON_PI) / ((double)RotationCount()))
+ : ON_DBL_QNAN;
+}
+
+const ON_Xform ON_Symmetry::Internal_RotationXform(
+ int rotation_index,
+ int rotation_count
+) const
+{
+ if (rotation_index < 0 || rotation_index >= rotation_count)
+ return ON_Xform::Nan;
+ if (0 == rotation_index)
+ return ON_Xform::IdentityTransformation;
+ if (1 == rotation_index)
+ return m_cyclic_transform;
+
+ return ON_Symmetry::Internal_RotationXform(m_rotation_axis, rotation_index, rotation_count);
+}
+
+const ON_Xform ON_Symmetry::Internal_RotationXform(
+ ON_Line rotation_axis,
+ int rotation_index,
+ int rotation_count
+)
+{
+ if (rotation_index < 0 || rotation_index >= rotation_count)
+ return ON_Xform::Nan;
+ if (0 == rotation_index)
+ return ON_Xform::IdentityTransformation;
+
+ // calculate from trig functions for maximum precision
+ double sin_sign = 1.0;
+ if (2 * rotation_index > rotation_count)
+ {
+ rotation_index = rotation_count - rotation_index;
+ sin_sign = -1.0;
+ }
+
+ double cos_angle = ON_DBL_QNAN;
+ double sin_angle = ON_DBL_QNAN;
+
+ if (2 * rotation_index == rotation_count)
+ {
+ // angle = pi
+ sin_angle = 0.0;
+ cos_angle = -1.0;
+ }
+ else if (4 * rotation_index == rotation_count)
+ {
+ // angle = pi/2
+ sin_angle = 1.0;
+ cos_angle = 0.0;
+ }
+ else if (6 * rotation_index == rotation_count)
+ {
+ // angle = pi/3
+ sin_angle = 0.5*sqrt(3.0);
+ cos_angle = 0.5;
+ }
+ else if (8 * rotation_index == rotation_count)
+ {
+ // angle = pi/4
+ sin_angle = cos_angle = 1.0 / sqrt(2.0);
+ }
+ else if (12 * rotation_index == rotation_count)
+ {
+ // angle = pi/3
+ sin_angle = 0.5;
+ cos_angle = 0.5*sqrt(3.0);
+ }
+ else
+ {
+ const double a = (rotation_index*(2.0*ON_PI)) / ((double)rotation_count);
+ sin_angle = sin(a);
+ cos_angle = cos(a);
+ }
+
+ ON_Xform r;
+ r.Rotation(sin_sign*sin_angle, cos_angle, rotation_axis.Direction(), rotation_axis.from);
+ return r;
+}
+
+const ON_Xform ON_Symmetry::MotifTransformation(
+ int index
+) const
+{
+ const int count = MotifCount();
+ if ( count <= 1)
+ return ON_Xform::Nan;
+
+ // convert index to be >= 0
+ index = ((index % count) + count) % count;
+
+ ON_Xform x = ON_Xform::Nan;
+ switch (m_type)
+ {
+ case ON_Symmetry::Type::Unset:
+ break;
+
+ case ON_Symmetry::Type::Reflect:
+ x = (0 == index)
+ ? ON_Xform::IdentityTransformation
+ : m_inversion_transform;
+ break;
+
+ case ON_Symmetry::Type::Rotate:
+ x = Internal_RotationXform(index, count);
+ break;
+
+ case ON_Symmetry::Type::ReflectAndRotate:
+ if (0 == index)
+ x = ON_Xform::IdentityTransformation;
+ else if (1 == index)
+ x = m_inversion_transform;
+ else if (2 == index)
+ x = m_cyclic_transform;
+ else if ( index > 2 )
+ x = Internal_ReflectAndRotateTransformation((unsigned)index);
+ break;
+
+ case ON_Symmetry::Type::Inversion:
+ x = (0 == index)
+ ? ON_Xform::IdentityTransformation
+ : m_inversion_transform;
+ break;
+
+ case ON_Symmetry::Type::Cyclic:
+ if (0 == index)
+ {
+ x = ON_Xform::IdentityTransformation;
+ }
+ else if (1 == index)
+ {
+ x = m_cyclic_transform;
+ }
+ else if (index >= 2)
+ {
+ x = m_cyclic_transform * m_cyclic_transform;
+ for (int i = 2; i < index; i++)
+ x = m_cyclic_transform * x;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return x;
+}
+
+
+const ON_Xform ON_Symmetry::Internal_ReflectAndRotateTransformation(unsigned index) const
+{
+ ON_Xform r = Internal_RotationXform(index / 2, m_cyclic_order);
+ if (1 == index % 2)
+ r = r * m_inversion_transform;
+ return r;
+}
diff --git a/opennurbs_symmetry.h b/opennurbs_symmetry.h
new file mode 100644
index 00000000..9cca2149
--- /dev/null
+++ b/opennurbs_symmetry.h
@@ -0,0 +1,479 @@
+/* $NoKeywords: $ */
+/*
+//
+// Copyright (c) 1993-2014 Robert McNeel & Associates. All rights reserved.
+// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
+// McNeel & Associates.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
+// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
+// MERCHANTABILITY ARE HEREBY DISCLAIMED.
+//
+// For complete openNURBS copyright information see .
+//
+////////////////////////////////////////////////////////////////
+*/
+
+#if !defined(OPENNURBS_SYMMETRY_INC_)
+#define OPENNURBS_SYMMETRY_INC_
+
+//////////////////////////////////////////////////////////////////////////
+//
+// ON_Symmetry
+//
+class ON_CLASS ON_Symmetry
+{
+public:
+ ON_Symmetry() = default;
+ ~ON_Symmetry() = default;
+ ON_Symmetry(const ON_Symmetry&) = default;
+ ON_Symmetry& operator=(const ON_Symmetry&) = default;
+
+public:
+ static const ON_Symmetry Unset;
+
+ enum : unsigned int
+ {
+ MaximumOrder = 4096
+ };
+
+ enum class Type : unsigned char
+ {
+ Unset = 0,
+
+ ///
+ /// Reflection about a plane.
+ /// The symmetric object has 2 copies of the motif.
+ /// Points on the reflection plane are fixed.
+ ///
+ Reflect = 1,
+
+ ///
+ /// Rotation around an axis.
+ /// The symmetric object has reflection copies of the motif.
+ /// Points on the axis are fixed.
+ ///
+ Rotate = 2,
+
+ ///
+ /// Reflection and rotation alternate.
+ /// Rotation angle is 360/N degrees
+ /// The symmetric object has 2N copies of the motif.
+ /// Points on the rotation axis are fixed.
+ ///
+ ReflectAndRotate = 3,
+
+ ///
+ /// General case inversion symmetry.
+ /// (inversion transformation)^2 = identity.
+ /// Det(inversion transformation) = -1.
+ /// The symmetric object has 2 copies of the motif.
+ ///
+ Inversion = 4,
+
+ ///
+ /// General case cyclic symmtry (order >= 2)
+ /// (cyclic transformation)^N = identity.
+ /// When N is 2 or odd, Det(cyclic transformation) = 1.
+ /// When N is even and greater than 2, Det(cyclic transformation) = 1 or -1.
+ /// The symmetric object has N copies of the motif.
+ ///
+ Cyclic = 5,
+ };
+
+ static ON_Symmetry::Type SymmetryTypeFromUnsigned(unsigned int symmetry_type_as_unsigned);
+
+ static const ON_wString SymmetryTypeToString(
+ ON_Symmetry::Type symmetry_type
+ );
+
+
+ enum class Coordinates : unsigned char
+ {
+ Unset = 0,
+
+ ///
+ /// The symmetry is associated with an object is is applied to.
+ /// If that object is transformed, the symmetry's planes and rotation axes
+ /// are also transformed.
+ ///
+ Object = 1,
+
+ ///
+ /// The symmetry is indepenent of any objects is it applied to.
+ /// The symmetry's planes and rotation axes are not changed when
+ /// any of those objects are transformed.
+ ///
+ World = 2,
+ };
+
+ static ON_Symmetry::Coordinates SymmetryCoordinatesFromUnsigned(unsigned int coordinates_as_unsigned);
+
+ static const ON_wString SymmetryCoordinatesToString(
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ static const ON_UUID ReflectId;
+ static const ON_UUID RotateId;
+ static const ON_UUID ReflectAndRotateId;
+
+ /*
+ Description:
+ Reflection about a plane.
+ The symmetric object has 2 copies of the motif.
+ Points on the reflection plane are fixed.
+ Parameters:
+ reflection_plane - [in]
+ symmetry_coordinates - [in]
+ object or world.
+ Example:
+ If the reflection_plane is the y-z plane,
+ then (x,y,z) -> (-x,y,z) -> (x,y,z).
+ Remarks:
+ InversionTransform() = reflection.
+ CyclicTransform() = identity.
+ */
+ static const ON_Symmetry CreateReflectSymmetry(
+ ON_PlaneEquation reflection_plane,
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ /*
+ Description:
+ Rotation around an axis.
+ The symmetric object has reflection copies of the motif.
+ Points on the axis are fixed.
+ Parameters:
+ rotation_axis - [in]
+ rotation_count - [in]
+ rotation_count must be >= 2 and the rotation angle is (360/rotation_count) degrees.
+ symmetry_coordinates - [in]
+ object or world.
+ Example:
+ If the rotation axis is the z-axis and the order is N, then
+ then (x,y,z) -> (r*cos(a), r*sin(a), z) -> (r*cos(2*a), r*sin(2*a), z) -> ... -> (x,y,z),
+ where r = sqrt(x*x + y*y) and a = (360 degrees)/N.
+ Remarks:
+ CyclicTransform() = rotation.
+ InversionTransform() = identity.
+ */
+ static const ON_Symmetry CreateRotateSymmetry(
+ ON_Line rotation_axis,
+ unsigned int rotation_count,
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ /*
+ Description:
+ Reflection and rotation alternate.
+ The symmetric object has 2*reflection copies of the motif.
+ Points on the rotation axis are fixed.
+ Parameters:
+ reflection_plane - [in]
+ rotation_axis - [in]
+ A line in the reflection plane.
+ rotation_count - [in]
+ rotation_count must be >= 2 and the rotation angle is (360/rotation_count) degrees.
+ symmetry_coordinates - [in]
+ object or world.
+ Example:
+ If the reflection_plane is the y-z plane, the rotation axis is the z-axis,
+ and rotation_count = 2,
+ then (x,y,z) -> (-x,y,z) -> (-x-y,z) -> (x,-y,z) -> (x,y,z).
+ Remarks:
+ InversionTransform() = reflection.
+ CyclicTransform() = rotation by (360/rotation_count) degrees.
+ */
+ static const ON_Symmetry CreateReflectAndRotateSymmetry(
+ ON_PlaneEquation reflection_plane,
+ ON_Line rotation_axis,
+ unsigned int rotation_count,
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ /*
+ Description:
+ Create an inversion symmetry from a transformation.
+ The symmetric object has 2 copies of the modif.
+ Parameters:
+ symmetry_id - [in]
+ An id you can assign to the symmetry
+ inversion_transform - [in]
+ inversion_transform^2 = identity.
+ Det(inversion_transform) = -1.
+ symmetry_coordinates - [in]
+ object or world.
+ Remarks:
+ If inversion_transform is a reflection, consider using CreateReflectSymmetry() instead.
+ When Det(transformation) = 1 and transformtion^2 = identity, use CreateCyclicSymmetry() instead.
+ */
+ static const ON_Symmetry CreateInversionSymmetry(
+ ON_UUID symmetry_id,
+ ON_Xform inversion_transform,
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ /*
+ Description:
+ Create a cyclic symmetry from a transformation.
+ The symmetric object has order copies of the modif.
+ Parameters:
+ symmetry_id - [in]
+ An id you can assign to the symmetry
+ cyclic_transform - [in]
+ cyclic_transform^order = identity
+ cyclic_transform^i != identity when 0 < i < order
+ If order is even and at least 4, then Det(cyclic_transform) = 1 or -1.
+ Otherwise Det(cyclic_transform) = 1.
+ cyclic_order - [in]
+ cyclic_order >= 2
+ symmetry_coordinates - [in]
+ object or world.
+ Remarks:
+ If cyclic_transform is a rotation, use CreateRotationSymmetry().
+ If cyclic_transform is a reflection, use CreateReflectionSymmetry().
+ If 2 = cyclic_order and Det(cyclic_transform) = -1, use CreateInversionSymmetry().
+ */
+ static const ON_Symmetry CreateCyclicSymmetry(
+ ON_UUID symmetry_id,
+ ON_Xform cyclic_transform,
+ unsigned int cyclic_order,
+ ON_Symmetry::Coordinates symmetry_coordinates
+ );
+
+ static int Compare(const ON_Symmetry* lhs, const ON_Symmetry* rhs);
+
+public:
+ /*
+ Returns:
+ Symmetry type.
+ */
+ ON_Symmetry::Type SymmetryType() const;
+
+ /*
+ Returns:
+ Symmetry type.
+ */
+ ON_Symmetry::Coordinates SymmetryCoordinates() const;
+
+ /*
+ Returns:
+ Symmetry unique id
+ */
+ const ON_UUID SymmetryId() const;
+
+ /*
+ Descripton:
+ Set this instance to ON_Symmetry::Unset.
+ */
+ void Clear();
+
+ /*
+ Rturns:
+ True if this instance is set to a symmetry.
+ */
+ bool IsSet() const;
+
+ /*
+ Rturns:
+ True if this instance is not set.
+ */
+ bool IsUnset() const;
+
+ /*
+ Returns:
+ Number of types the motif appears in the symmetric object.
+ Remarks:
+ MotifCount() = CyclicOrder()*InversionOrder().
+ */
+ unsigned int MotifCount() const;
+
+ /*
+ Returns:
+ 0: unset symmetry
+ 1: InversionTransform() = identity
+ 2: InversionTransform()^2 = identity and InversionTransform() != identity
+ Remarks:
+ In common cases, InversionTransform() is either the identity or a reflection.
+ */
+ unsigned int InversionOrder() const;
+
+ /*
+ Returns:
+ 0: unset symmetry
+ 1: the cyclic transformation is the identity.
+ N >= 2: CyclicTransform()^N = idenity and CyclicTransform()^i != idenity when 0 < i < N.
+ Remarks:
+ In common cases, CyclicTransform() is either the identity or a rotation.
+ */
+ unsigned int CyclicOrder() const;
+
+ /*
+ Description:
+ Get the transformation that maps the starting motif to the specified copy.
+ Parameters:
+ index - [in]
+ 0 based index of the copy (negative values are supported)
+ Remarks:
+ "0 based" means when index is a multiple of MotifCount(), the identity is returned.
+ */
+ const ON_Xform MotifTransformation(
+ int index
+ ) const;
+
+ /*
+ Returns:
+ The inversion transformation is returned.
+ InversionTransform()^InversionOrder() = identity.
+ Remarks:
+ In common cases, InversionTransform() is either the identity or a reflection (mirror).
+ Remarks:
+ NOTE: A symmetry with SymmetryOrder() = 2 and transformation S can be represented
+ as either InversionTransform() = S and CyclicTransform() = identity or
+ or CyclicTransform() = S and InversionTransform() = idenity.
+ The convention is to use the cyclic transforms when S is a 180 rotations
+ and inversion transforms otherwise.
+ */
+ const ON_Xform InversionTransform() const;
+
+ /*
+ Returns:
+ The cyclic transformation is returned.
+ CyclicTransform()^CyclicOrder() = identity.
+ Remarks:
+ In common cases, CyclicTransform() is either the identity or a rotation.
+ Remarks:
+ NOTE: A symmetry with SymmetryOrder() = 2 and transformation S can be represented
+ as either InversionTransform() = S and CyclicTransform() = identity or
+ or CyclicTransform() = S and InversionTransform() = idenity.
+ The convention is to use the cyclic transforms when S is a 180 rotations
+ and inversion transforms otherwise.
+ */
+ const ON_Xform CyclicTransform() const;
+
+ /*
+ Returns:
+ A SHA1 hash uniquely identifying the symmetry
+ */
+ const ON_SHA1_Hash Hash() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Reflect or ReflectAndRotate, then the reflection plane is returned.
+ Othewise ON_Plane::Nan is returned.
+ */
+ const ON_PlaneEquation ReflectionPlane() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then the rotation axis is returned.
+ Othewise ON_Line::Nan is returned.
+ */
+ const ON_Line RotationAxis() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then a point on the rotation axis is returned.
+ Othewise ON_3dPoint::Nan is returned.
+ */
+ const ON_3dPoint RotationAxisPoint() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then the direction of the rotation axis is returned.
+ Othewise ON_3dVector::Nan is returned.
+ Remarks:
+ This vector may have length != 1
+ */
+ const ON_3dVector RotationAxisDirection() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then a unit vector in the direction of the rotation axis is returned.
+ Othewise ON_3dVector::Nan is returned.
+ */
+ const ON_3dVector RotationAxisTangent() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then the rotation count is returned.
+ Othewise 0 is returned.
+ */
+ unsigned int RotationCount() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then the rotation angle in degrees is returned.
+ Othewise ON_DBL_QNAN is returned.
+ Remarks:
+ RotationAngleDegrees() = 360.0/RotationCount()
+ */
+ double RotationAngleDegrees() const;
+
+ /*
+ Returns:
+ If the symmetry is type is Rotate or ReflectAndRotate, then the rotation angle in radians is returned.
+ Othewise ON_DBL_QNAN is returned.
+ Remarks:
+ RotationAngleRadians() = (2.0*ON_PI)/RotationCount()
+ */
+ double RotationAngleRadians() const;
+
+public:
+ bool Write(class ON_BinaryArchive&) const;
+ bool Read(class ON_BinaryArchive&);
+ void Dump(class ON_TextLog&) const;
+
+ /*
+ Description:
+ If SymmetryCoordinates() = ON_Symmetry::Coordinates::Object, then the symmetry defition
+ is transformed.
+ */
+ const ON_Symmetry TransformConditionally(const ON_Xform& xform) const;
+
+ /*
+ Description:
+ The the symmetry defition is transformed.
+ */
+ const ON_Symmetry TransformUnconditionally(const ON_Xform& xform) const;
+
+private:
+ static const double ZeroTolerance;
+
+ ON_Symmetry::Type m_type = ON_Symmetry::Type::Unset;
+
+ ON_Symmetry::Coordinates m_coordinates = ON_Symmetry::Coordinates::Unset;
+
+ // m_inversion_order (0 = unset, 1 = identity (no inversion), 2 = (non-identity inversion)
+ unsigned char m_inversion_order = 0;
+
+ unsigned char m_reserved1 = 0;
+
+ // m_cyclic_order (0 = unset, 1 = identity (no cyclic), >= 2 cyclic order (non-identity cyclic)
+ unsigned int m_cyclic_order = 0;
+
+ ON__UINT_PTR m_reserved2 = 0;
+
+ // id is a preset value for the 3 built in symmetries and user defined for others
+ ON_UUID m_id = ON_nil_uuid;
+
+ // m_inversion_transform^2 = identity
+ ON_Xform m_inversion_transform = ON_Xform::Nan;
+
+ // m_cyclic_transform^m_cyclic_order = identity
+ ON_Xform m_cyclic_transform = ON_Xform::Nan;
+
+ // Set when type is Reflect or ReflectAndRotate
+ ON_PlaneEquation m_reflection_plane = ON_PlaneEquation::NanPlaneEquation;
+
+ // Set when type is Rotate or ReflectAndRotate
+ ON_Line m_rotation_axis = ON_Line::NanLine;
+
+private:
+ static int Internal_CompareDouble(const double* lhs, const double* rhs, size_t count);
+ const ON_Xform Internal_ReflectAndRotateTransformation(unsigned index) const;
+ static const ON_Xform Internal_RotationXform(ON_Line axis, int rotation_index, int rotation_count);
+ const ON_Xform Internal_RotationXform(int rotation_index, int rotation_count) const;
+};
+
+#endif
diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp
index 824c6476..e204a0e6 100644
--- a/opennurbs_text.cpp
+++ b/opennurbs_text.cpp
@@ -41,6 +41,7 @@ void ON_TextContent::Internal_Destroy()
m_annotation_type = ON_TextContent::Empty.m_annotation_type;
m_dimstyle_text_position_properties_hash = ON_TextContent::Empty.m_dimstyle_text_position_properties_hash;
Internal_ClearTextContentHash();
+ m_default_font = &ON_Font::Default;
}
ON_TextContent::~ON_TextContent()
@@ -79,6 +80,9 @@ void ON_TextContent::Internal_CopyFrom(
{
m__wrapped_runs = new ON_TextRunArray(*src.m__wrapped_runs);
}
+
+ m_default_font = src.m_default_font;
+
m_runtime_halign = src.m_runtime_halign;
}
@@ -342,14 +346,17 @@ bool ON_TextContent::Internal_ParseRtf(
bool rc = parser.Parse();
if (rc)
rc = ON_TextContent::MeasureTextContent(this, true, false);
+ if (rc)
+ m_default_font = &dimstyle->Font();
if (rc && bComposeAndUpdateRtf)
{
- rc = RtfComposer::Compose(this, dimstyle, default_fontname, str);
+ rc = RtfComposer::Compose(this, str, false);
if (rc)
{
m_text = str;
}
}
+
return rc;
}
@@ -358,6 +365,33 @@ const ON_wString ON_TextContent::RichText() const
return m_text;
}
+const ON_wString ON_TextContent::RichTextFromRuns(ON::RichTextStyle rich_text_style) const
+{
+ ON_wString rich_text;
+
+ switch (rich_text_style)
+ {
+ case ON::RichTextStyle::Windows10SDK:
+ if (!RtfComposer::Compose(this, rich_text, true))
+ rich_text.Empty();
+ break;
+
+ case ON::RichTextStyle::AppleOSXSDK:
+ rich_text = RtfComposer::ComposeAppleRTF(this);
+ break;
+
+ default:
+ break;
+ };
+
+ return rich_text;
+}
+
+const ON_wString ON_TextContent::PlatformRichTextFromRuns() const
+{
+ return RichTextFromRuns(ON::RichTextStyleFromCurrentPlatform());
+}
+
const ON_wString ON_TextContent::PlainTextWithFields() const
{
return Internal_GetPlainText(false, false);
@@ -1356,6 +1390,25 @@ ON_Mesh* ON_TextContent::Get3dPickMesh() const
return mesh;
}
+ON_Mesh* ON_TextContent::Get3dMaskPickMesh(double maskoffset) const
+{
+ ON_3dPoint corners[4];
+ if (Get3dMaskCorners(maskoffset, corners))
+ {
+ ON_Mesh* mesh = new ON_Mesh(1, 4, false, false);
+ if (nullptr != mesh)
+ {
+ mesh->SetVertex(0, corners[0]);
+ mesh->SetVertex(1, corners[1]);
+ mesh->SetVertex(2, corners[2]);
+ mesh->SetVertex(3, corners[3]);
+ mesh->SetQuad(0, 0, 1, 2, 3);
+ return mesh;
+ }
+ }
+ return Get3dPickMesh();
+}
+
bool ON_TextContent::Get2dSize(bool raw, double& width, double& height) const
{
@@ -1386,8 +1439,7 @@ bool ON_TextContent::Get3dCorners(ON_3dPoint corners[4]) const
{
if (nullptr == corners)
return false;
- bool rc = false;
- ON_2dPoint corners2d[4];
+ bool rc = false; ON_2dPoint corners2d[4];
if (Get2dCorners(corners2d))
{
corners[0] = ON_Plane::World_xy.PointAt(corners2d[0].x, corners2d[0].y);
@@ -1744,11 +1796,19 @@ const wchar_t* ON_TextContent::RtfText() const
return m_text;
}
+const ON_Font& ON_TextContent::DefaultFont() const
+{
+ if (nullptr != m_default_font)
+ return *m_default_font;
+ else
+ return ON_Font::Default;
+}
+
bool ON_TextContent::ComposeText()
{
ON_wString rtf;
ON_wString nothing;
- if (RtfComposer::Compose(this, nullptr, nothing, rtf))
+ if (RtfComposer::Compose(this, rtf, false))
{
m_text = rtf;
return true;
@@ -2478,7 +2538,28 @@ bool ON_TextContent::FormatArea(
bool alt,
ON_wString& formatted_string)
{
- double area = area_in;
+ return ON_TextContent::FormatAreaOrVolume(area_in, true, units_in, dimstyle, alt, formatted_string);
+}
+
+bool ON_TextContent::FormatVolume(
+ double volume_in,
+ ON::LengthUnitSystem units_in,
+ const ON_DimStyle* dimstyle,
+ bool alt,
+ ON_wString& formatted_string)
+{
+ return ON_TextContent::FormatAreaOrVolume(volume_in, false, units_in, dimstyle, alt, formatted_string);
+}
+
+bool ON_TextContent::FormatAreaOrVolume(
+ double area_or_volume_in,
+ bool format_area,
+ ON::LengthUnitSystem units_in,
+ const ON_DimStyle* dimstyle,
+ bool alt,
+ ON_wString& formatted_string)
+{
+ double value = area_or_volume_in;
if (nullptr == dimstyle)
dimstyle = &ON_DimStyle::Default;
@@ -2499,21 +2580,23 @@ bool ON_TextContent::FormatArea(
: dimstyle->LengthFactor();
double unit_length_factor = ON::UnitScale(units_in, dim_us);
- area = unit_length_factor * unit_length_factor * length_factor * area_in;
+ value = format_area ?
+ unit_length_factor * unit_length_factor * length_factor * area_or_volume_in :
+ unit_length_factor * unit_length_factor * unit_length_factor * length_factor * area_or_volume_in;
double roundoff = alt ? dimstyle->AlternateRoundOff() : dimstyle->RoundOff();
int precision = alt ? dimstyle->AlternateLengthResolution() : dimstyle->LengthResolution();
ON_DimStyle::suppress_zero zs = alt ? dimstyle->AlternateZeroSuppress() : dimstyle->ZeroSuppress();
- if (fabs(area) < pow(10.0, -(precision + 1)))
- area = 0.0;
+ if (fabs(value) < pow(10.0, -(precision + 1)))
+ value = 0.0;
wchar_t decimal_char = dimstyle->DecimalSeparator();
ON_DimStyle::OBSOLETE_length_format output_format = ON_DimStyle::OBSOLETE_length_format::Decimal;
bool rc = ON_NumberFormatter::FormatNumber(
- area,
+ value,
output_format,
roundoff,
precision,
diff --git a/opennurbs_text.h b/opennurbs_text.h
index 178c4d9e..99950f21 100644
--- a/opennurbs_text.h
+++ b/opennurbs_text.h
@@ -189,11 +189,33 @@ public:
/*
Returns:
- Raw text that can contain rich text formatting instructions.
+ Rhino clean rich text that is currently saved on this instance.
+ The text can contain text formatting instructions.
Fields are not evaluated.
*/
const ON_wString RichText() const;
+ /*
+ Returns:
+ Rich text suitable for initializing SDK controls on the current platform (Windows or Apple OS X).
+ The returned string is alwasy rich text (never plain text).
+ The returned rich text is always generated directly from the runs.
+ This text is typically different from the Rhino clean rich text returned by the RichText() command.
+ */
+ const ON_wString PlatformRichTextFromRuns() const;
+
+
+ /*
+ Parameters:
+ rich_text_style - [in]
+ Type of rich text to return.
+ Returns:
+ The returned string is alwasy rich text (never plain text).
+ The returned rich text is always generated directly from the runs.
+ This text is typically different from the Rhino clean rich text returned by the RichText() command.
+ */
+ const ON_wString RichTextFromRuns(ON::RichTextStyle rich_text_style) const;
+
/*
Returns:
Plain text information with any rich text formatting instructions removed.
@@ -229,6 +251,7 @@ public:
*/
const ON_wString WrappedPlainTextWithFields() const;
+
private:
void Internal_SetRunTextHeight(double height);
@@ -324,6 +347,7 @@ public:
ON_Mesh* Get2dPickMesh() const;
ON_Mesh* Get3dPickMesh() const;
+ ON_Mesh* Get3dMaskPickMesh(double maskoffset) const;
// Returns pointer to either m_runs, the basic parsed and evaluated text
// or m_wrapped_runs which is the runs after text wrapping
@@ -353,6 +377,10 @@ public:
//void SetCurrentDimStyle(const ON_DimStyle* dimstyle) const;
+ // Returns the default font for this text content.
+ // Typically, that would be the font from the dimstyle
+ const ON_Font& DefaultFont() const;
+
ON::TextHorizontalAlignment RuntimeHorizontalAlignment() const;
void SetRuntimeHorizontalAlignment(ON::TextHorizontalAlignment halign) const;
@@ -399,7 +427,13 @@ private:
mutable ON_SHA1_Hash m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest;
mutable ON_BoundingBox m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox;
- ON__INT_PTR m_reserved0 = (ON__INT_PTR)0;
+ // Stores a pointer to the top level installed font for the entire textContent
+ // May be overridden by font in individual runs
+ // Dec. 12, 2019
+ mutable const ON_Font* m_default_font = &ON_Font::Default;
+ //ON__INT_PTR m_reserved0 = (ON__INT_PTR)0;
+
+
public:
friend class ON_Text;
@@ -556,14 +590,22 @@ public:
bool alternate, // Primary or alternate
ON_wString& formatted_string); // Output
- static bool FormatArea(
- double area,
- ON_DimStyle::LengthDisplay output_lengthdisplay,
- double round_off,
- int resolution,
- ON_DimStyle::suppress_zero zero_suppress,
- wchar_t decimal_char,
- ON_wString& output);
+ // Volume formatting
+ static bool FormatVolume(
+ double volume,
+ ON::LengthUnitSystem units_in,
+ const ON_DimStyle* dimstyle,
+ bool alternate, // Primary or alternate
+ ON_wString& formatted_string); // Output
+
+ private:
+ static bool FormatAreaOrVolume(
+ double area_or_volume,
+ bool format_area,
+ ON::LengthUnitSystem units_in,
+ const ON_DimStyle* dimstyle,
+ bool alternate, // Primary or alternate
+ ON_wString& formatted_string); // Output
};
diff --git a/opennurbs_textiterator.cpp b/opennurbs_textiterator.cpp
index 5177b454..4fbd800a 100644
--- a/opennurbs_textiterator.cpp
+++ b/opennurbs_textiterator.cpp
@@ -2840,7 +2840,7 @@ bool ON_RtfParser::Parse()
// This has to make a string that the text control can digest
// and that's short and simple as possible so it can be put in the file
-unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_SimpleArray< wchar_t[34] >& fonttable)
+unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_ClassArray< ON_wString >& fonttable)
{
if (nullptr == font)
return 0;
@@ -2850,16 +2850,15 @@ unsigned int RtfComposer::GetFacenameKey(const ON_Font* font, ON_SimpleArray< wc
if (rtf_facename.IsEmpty())
return 0;
+#if defined(ON_RUNTIME_WIN)
if (rtf_facename.Length() > 33)
- ON_ERROR("rtf_facename is too long\n");
- wchar_t facename[34];
- wcsncpy(facename, rtf_facename.Array(), 33);
- facename[33] = 0;
+ ON_ERROR("rtf_facename is longer than Windows LogfontName\n");
+#endif
- return RtfComposer::GetFacenameKey(facename, fonttable);
+ return RtfComposer::GetFacenameKey(rtf_facename.Array(), fonttable);
}
-unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable)
+unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_ClassArray< ON_wString >& fonttable)
{
if(nullptr == facename || 0 == facename[0])
return 0;
@@ -2871,12 +2870,9 @@ unsigned int RtfComposer::GetFacenameKey(const wchar_t* facename, ON_SimpleArray
if(ON_wString::EqualOrdinal(facename, fonttable[i], true))
return i;
}
- if (nullptr != wcsncpy(fonttable.AppendNew(), facename, 33))
- {
- fonttable[count][33] = 0;
- return count;
- }
- return 0;
+
+ fonttable.AppendNew() = facename;
+ return count;
}
unsigned int RtfComposer::GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable)
{
@@ -2977,15 +2973,11 @@ static bool GetRunText(ON_TextRun* run, ON_wString& text_out, bool& foundunicode
return true;
}
-
bool RtfComposer::Compose(
const ON_TextContent* text,
- const ON_DimStyle* dimstyle,
- const ON_wString default_fontname,
- ON_wString& rtf)
+ ON_wString& rtf,
+ bool bForceRtf)
{
- dimstyle = &ON_DimStyle::DimStyleOrDefault(dimstyle);
-
if (0 == text)
return false;
@@ -2999,14 +2991,10 @@ bool RtfComposer::Compose(
if (nullptr == runs)
return false;
- //if (dimstyle->IsChildDimstyle())
- //{
- // ON_UUID parent_id = dimstyle->ParentId();
- //
- //}
-
-
- const ON_Font& style_font = dimstyle->Font();
+ const ON_Font& style_font = text->DefaultFont();
+ const ON_wString style_fontname = style_font.RichTextFontName();
+ if (style_fontname.IsEmpty())
+ return false;
bool style_bold = style_font.IsBoldInQuartet();
bool style_italic = (ON_Font::Style::Italic == style_font.FontStyle());
@@ -3018,21 +3006,17 @@ bool RtfComposer::Compose(
bool chg_underline = false;
bool chg_strikeout = false;
bool chg_facename = false;
- if (dimstyle->IsChildDimstyle() && dimstyle->IsFieldOverride(ON_DimStyle::field::Font))
- chg_facename = true;
-
+
// First facename is from the ON_TextContent
// Any after that are from runs
- ON_SimpleArray< wchar_t[34] > fonttable(8);
-
+ ON_ClassArray< ON_wString > fonttable(8);
+
// Creates a fonttable entry the first time
- unsigned int deffont_key = default_fontname.IsNotEmpty() ?
- GetFacenameKey(default_fontname, fonttable) :
- GetFacenameKey(&style_font, fonttable);
+ unsigned int stylefont_key = GetFacenameKey(style_fontname, fonttable);
int runcount = runs->Count();
int nlcount = 0;
-
+
// See if this is multi-line text
bool multiline = false;
for (int ri = 0; ri < runcount; ri++)
@@ -3041,14 +3025,17 @@ bool RtfComposer::Compose(
if (nullptr != run)
{
if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount)
+ {
multiline = true;
+ break;
+ }
else if (ON_TextRun::RunType::kNewline == run->Type() ||
ON_TextRun::RunType::kParagraph == run->Type())
nlcount++;
}
}
-
- bool make_rtf = false;
+
+ bool make_rtf = bForceRtf;
ON_SimpleArray< ON_TextRun* > runholders;
for (int ri = 0; ri < runcount; ri++)
{
@@ -3064,25 +3051,29 @@ bool RtfComposer::Compose(
if (nullptr != run_font)
{
runholders.AppendNew() = run;
- if(!chg_bold)
+ if (!chg_bold)
chg_bold = run_font->IsBoldInQuartet() != style_bold;
- if(!chg_italic)
+ if (!chg_italic)
chg_italic = run_font->IsItalic() != style_italic;
- if(!chg_underline)
+ if (!chg_underline)
chg_underline = run_font->IsUnderlined() != style_underline;
- if(!chg_strikeout)
+ if (!chg_strikeout)
chg_strikeout = run_font->IsStrikethrough() != style_strikeout;
- unsigned int facename_key = GetFacenameKey(run_font, fonttable);
- if(!chg_facename)
- chg_facename = facename_key != deffont_key;
+ const ON_wString& run_fontname = run_font->RichTextFontName();
+ if (run_fontname.IsEmpty())
+ return false;
+ unsigned int run_font_key = GetFacenameKey(run_fontname, fonttable);
+
+ if (!chg_facename)
+ chg_facename = run_font_key != stylefont_key;
}
const ON__UINT32* run_codepoints = run->UnicodeString();
if (!make_rtf && nullptr != run_codepoints)
{
for (int i = 0; run_codepoints[i] != 0; i++)
{
- if (run_codepoints[i] > 127
+ if (run_codepoints[i] > 127
|| run_codepoints[i] == '\\'
|| run_codepoints[i] == '{'
|| run_codepoints[i] == '}'
@@ -3102,7 +3093,6 @@ bool RtfComposer::Compose(
runholders.AppendNew() = run;
}
}
-
} // end of getting runinfo
if (chg_bold || chg_italic || chg_underline || chg_strikeout || chg_facename)
@@ -3133,18 +3123,18 @@ bool RtfComposer::Compose(
// add properties for this string
run_strings += L"{";
bool addspace = false;
+
unsigned int run_font_key = GetFacenameKey(run_font, fonttable);
- {
- temp.Format(L"\\f%d", run_font_key);
- run_strings += temp;
- addspace = true;
- }
+ temp.Format(L"\\f%d", run_font_key);
+ run_strings += temp;
+ addspace = true;
+
if (run_font->IsBoldInQuartet())
{
run_strings += L"\\b";
addspace = true;
}
- else if(style_bold)
+ else if (style_bold)
{
run_strings += L"\\b0";
addspace = true;
@@ -3200,7 +3190,7 @@ bool RtfComposer::Compose(
GetRunText(run, run_strings, make_rtf);
}
}
- else if (ri < runcount-1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type()))
+ else if (ri < runcount - 1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type()))
{
if (make_rtf)
run_strings += L"{\\par}";
@@ -3227,13 +3217,13 @@ bool RtfComposer::Compose(
if (0 < nfont)
{
ON_wString fonttable_string;
- temp.Format(L"\\deff%d", deffont_key);
+ temp.Format(L"\\deff%d", stylefont_key);
rtf += temp;
fonttable_string = L"{\\fonttbl";
for (int fi = 0; fi < nfont; fi++)
{
//
- temp.Format(L"{\\f%d %s;}", fi, fonttable[fi]);
+ temp.Format(L"{\\f%d %s;}", fi, fonttable[fi].Array());
fonttable_string += temp;
}
rtf += fonttable_string;
@@ -3269,3 +3259,183 @@ void RtfComposer::SetRecomposeRTF(bool b)
RtfComposer::m_bComposeRTF = b;
}
+const ON_wString RtfComposer::ComposeAppleRTF(
+ const ON_TextContent* text)
+{
+ ON_wString rtf_string;
+
+ if (0 == text)
+ return rtf_string;
+
+ ON_TextRunArray* runs = text->TextRuns(true);
+ if (nullptr == runs)
+ return rtf_string;
+
+
+ const ON_Font& style_font = text->DefaultFont();
+ const ON_wString style_fontname = style_font.PostScriptName();
+ if (style_fontname.IsEmpty())
+ return rtf_string;
+
+ // First facename is from the ON_TextContent
+ // Any after that are from runs
+ ON_ClassArray< ON_wString > fonttable(8);
+
+ // Creates a fonttable entry the first time
+ unsigned int deffont_key = GetFacenameKey(style_fontname, fonttable);
+
+ int runcount = runs->Count();
+ int nlcount = 0;
+
+ // See if this is multi-line text
+ bool multiline = false;
+ for (int ri = 0; ri < runcount; ri++)
+ {
+ ON_TextRun* run = (*runs)[ri];
+ if (nullptr != run)
+ {
+ if (ON_TextRun::RunType::kText == run->Type() && 0 < nlcount)
+ multiline = true;
+ else if (ON_TextRun::RunType::kNewline == run->Type() ||
+ ON_TextRun::RunType::kParagraph == run->Type())
+ nlcount++;
+ }
+ }
+
+ ON_SimpleArray< ON_TextRun* > runholders;
+ for (int ri = 0; ri < runcount; ri++)
+ {
+ ON_TextRun* run = (*runs)[ri];
+ if (nullptr != run)
+ {
+ if (
+ ON_TextRun::RunType::kText == run->Type() ||
+ ON_TextRun::RunType::kField == run->Type()
+ )
+ {
+ const ON_Font* run_font = run->Font();
+ if (nullptr != run_font)
+ {
+ runholders.AppendNew() = run;
+ }
+ }
+ else if (
+ ON_TextRun::RunType::kParagraph == run->Type() ||
+ ON_TextRun::RunType::kNewline == run->Type()
+ )
+ {
+ runholders.AppendNew() = run;
+ }
+ }
+ } // end of getting runinfo
+
+ ON_wString run_strings;
+ ON_wString temp;
+ runcount = runholders.Count();
+ for (int ri = 0; ri < runcount; ri++)
+ {
+ ON_TextRun* run = runholders[ri];
+ if (nullptr == run)
+ continue;
+
+ if (
+ ON_TextRun::RunType::kText == run->Type() ||
+ ON_TextRun::RunType::kField == run->Type()
+ )
+ {
+ const ON_Font* run_font = run->Font();
+ if (nullptr == run_font)
+ continue;
+
+ const ON_wString& run_fontname = run_font->PostScriptName();
+ if (run_fontname.IsEmpty())
+ return rtf_string;
+
+ // add properties for this string
+ run_strings += L"{";
+ bool addspace = false;
+
+ unsigned int run_font_key = GetFacenameKey(run_fontname, fonttable);
+ temp.Format(L"\\f%d", run_font_key);
+ run_strings += temp;
+ addspace = true;
+
+ if (run_font->IsBoldInQuartet())
+ {
+ run_strings += L"\\b";
+ addspace = true;
+ }
+ if (run_font->IsItalic())
+ {
+ run_strings += L"\\i";
+ addspace = true;
+ }
+ if (run_font->IsUnderlined())
+ {
+ run_strings += L"\\ul";
+ addspace = true;
+ }
+ if (addspace)
+ run_strings += L" ";
+
+ bool true_bool = true;
+ if (run->IsStacked() == ON_TextRun::Stacked::kStacked && run->m_stacked_text != 0)
+ {
+ run_strings += L"[[";
+ run_strings += run->m_stacked_text->m_separator;
+ GetRunText(run->m_stacked_text->m_top_run, run_strings, true_bool);
+ run_strings += run->m_stacked_text->m_separator;
+ GetRunText(run->m_stacked_text->m_bottom_run, run_strings, true_bool);
+ run_strings += L"]]";
+ }
+ else if (ON_TextRun::RunType::kField == run->Type())
+ {
+ run_strings += L"%<";
+ GetRunText(run, run_strings, true_bool);
+ run_strings += L">%";
+ }
+ else
+ {
+ GetRunText(run, run_strings, true_bool);
+ }
+ run_strings += L"}";
+ }
+ else if (ri < runcount - 1 && multiline && (ON_TextRun::RunType::kNewline == run->Type() || ON_TextRun::RunType::kParagraph == run->Type()))
+ {
+ run_strings += L"{\\par}";
+ }
+ } // end of collecting run text
+
+ int nfont = fonttable.Count();
+ if (run_strings.Length() > 0)
+ {
+ // deff0 means use font0 for the default font throughout the string.
+ // If we include font0 in the font table, when we send
+ // the string back to the RTF control, the font listed as
+ // font0 will be used instead of the default font for the
+ // style.
+ // So the default font is listed as 0 and there is no
+ // entry in the table for font0.
+
+ rtf_string.Format(L"{\\rtf1");
+ if (0 < nfont)
+ {
+ ON_wString fonttable_string;
+ temp.Format(L"\\deff%d", deffont_key);
+ rtf_string += temp;
+ fonttable_string = L"{\\fonttbl";
+ for (int fi = 0; fi < nfont; fi++)
+ {
+ temp.Format(L"{\\f%d %s;}", fi, fonttable[fi].Array());
+ fonttable_string += temp;
+ }
+ rtf_string += fonttable_string;
+ }
+
+ rtf_string += L"}\\fs40";
+
+ rtf_string += run_strings;
+ rtf_string += L"\\par}";
+ }
+ return rtf_string;
+}
diff --git a/opennurbs_textiterator.h b/opennurbs_textiterator.h
index 9a960764..67bf1183 100644
--- a/opennurbs_textiterator.h
+++ b/opennurbs_textiterator.h
@@ -874,12 +874,13 @@ public:
int m_facename_key = -1;
};
-
static bool Compose(
const ON_TextContent* text,
- const ON_DimStyle* dimstyle,
- const ON_wString default_fontname,
- ON_wString& rtf);
+ ON_wString& rtf,
+ bool bForceRtf);
+
+ static const ON_wString ComposeAppleRTF(
+ const ON_TextContent* text);
static bool RecomposeRTF();
static void SetRecomposeRTF(bool b);
@@ -889,8 +890,8 @@ private:
RtfComposer();
- static unsigned int GetFacenameKey(const ON_Font* font, ON_SimpleArray< wchar_t[34] >& fonttable);
- static unsigned int GetFacenameKey(const wchar_t* facename, ON_SimpleArray< wchar_t[34] >& fonttable);
+ static unsigned int GetFacenameKey(const ON_Font* font, ON_ClassArray< ON_wString >& fonttable);
+ static unsigned int GetFacenameKey(const wchar_t* facename, ON_ClassArray< ON_wString >& fonttable);
static unsigned int GetColorKey(ON_Color color, ON_SimpleArray< unsigned int >& colortable);
static bool FormatTextHeight(double height, ON_wString& str);
};
diff --git a/opennurbs_textlog.cpp b/opennurbs_textlog.cpp
index db1b0c12..d2d33cf3 100644
--- a/opennurbs_textlog.cpp
+++ b/opennurbs_textlog.cpp
@@ -1043,6 +1043,10 @@ bool ON_TextLog::IsTextHash() const
return (nullptr != dynamic_cast(this));
}
+bool ON_TextLog::IsNull() const
+{
+ return this == &ON_TextLog::Null;
+}
ON_StringMapType ON_TextHash::StringMapType() const
{
diff --git a/opennurbs_textlog.h b/opennurbs_textlog.h
index ab948b7a..ea04ecc3 100644
--- a/opennurbs_textlog.h
+++ b/opennurbs_textlog.h
@@ -52,6 +52,18 @@ public:
*/
static ON_TextLog Null;
+ /*
+ Returns:
+ True if this is ON_TextLog::Null.
+ */
+ bool IsNull() const;
+
+ /*
+ Returns:
+ True if the text is being used to calculate a geometric content SHA1 hash
+ and non-geometry information that varies based on time, computer, or user
+ should not be printed.
+ */
bool IsTextHash() const;
public:
diff --git a/opennurbs_viewport.cpp b/opennurbs_viewport.cpp
index 52f1368f..0ea10c4c 100644
--- a/opennurbs_viewport.cpp
+++ b/opennurbs_viewport.cpp
@@ -4942,195 +4942,3 @@ int ON_Viewport::InViewFrustum(
return 0;
return cr.InViewFrustum(count,p);
}
-
-class ON_CLASS ON_ViewportUserData : public ON_UserData
-{
- ON_OBJECT_DECLARE(ON_ViewportUserData);
-
-public:
- ON_ViewportUserData();
- ~ON_ViewportUserData() = default;
- ON_ViewportUserData(const ON_ViewportUserData& src);
- ON_ViewportUserData& operator=(const ON_ViewportUserData& src);
-
- static ON_ViewportUserData* FromObject(const ON_Object* obj);
- static ON_ViewportUserData* FromObject(ON_Object* obj, bool createAndAttachIfMissing);
-
- // override virtual ON_UserData::GetDescription function
- bool GetDescription(ON_wString& description) override;
-
- // override virtual ON_Object::SizeOf function
- unsigned int SizeOf() const override;
-
- // override virtual ON_Object::DataCRC function
- ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override;
-
- // override virtual ON_Object::Dump function
- void Dump(ON_TextLog& text_log) const override;
-
- // override virtual ON_UserData::Archive function
- bool WriteToArchive(
- const class ON_BinaryArchive& archive,
- const class ON_Object* parent_object
- ) const override;
-
- // override virtual ON_Object::Write function
- bool Write(ON_BinaryArchive& binary_archive) const override;
-
- // override virtual ON_Object::Read function
- bool Read(ON_BinaryArchive& binary_archive) override;
-
-public:
- // The description, or contents, of the viewport.
- ON_wString m_description;
-};
-
-ON_OBJECT_IMPLEMENT(ON_ViewportUserData, ON_UserData, "DA2B97F8-5B76-4C50-8504-9A7CFC8AD62C");
-
-ON_ViewportUserData::ON_ViewportUserData()
-{
- m_userdata_uuid = ON_CLASS_ID(ON_ViewportUserData);
- m_application_uuid = ON_opennurbs6_id;
- m_userdata_copycount = 1;
-}
-
-ON_ViewportUserData::ON_ViewportUserData(const ON_ViewportUserData& src)
- : ON_UserData(src)
- , m_description(src.m_description)
-{
- m_userdata_uuid = ON_CLASS_ID(ON_ViewportUserData);
- m_application_uuid = ON_opennurbs6_id;
- m_userdata_copycount = 1;
-}
-
-ON_ViewportUserData& ON_ViewportUserData::operator=(const ON_ViewportUserData& src)
-{
- if (this != &src)
- {
- ON_UserData::operator=(src);
- m_description = src.m_description;
- }
- return *this;
-}
-
-ON_ViewportUserData* ON_ViewportUserData::FromObject(const ON_Object* obj)
-{
- if (nullptr == obj)
- return nullptr;
- return ON_ViewportUserData::Cast(obj->GetUserData(ON_CLASS_ID(ON_ViewportUserData)));
-}
-
-ON_ViewportUserData* ON_ViewportUserData::FromObject(ON_Object* obj, bool createAndAttachIfMissing)
-{
- if (nullptr == obj)
- return nullptr;
- ON_ViewportUserData* rc = ON_ViewportUserData::Cast(obj->GetUserData(ON_CLASS_ID(ON_ViewportUserData)));
- if (nullptr == rc && createAndAttachIfMissing)
- {
- rc = new ON_ViewportUserData();
- if (!obj->AttachUserData(rc))
- {
- delete rc;
- return nullptr;
- }
- }
- return rc;
-}
-
-bool ON_ViewportUserData::GetDescription(ON_wString& description)
-{
- description = L"Viewport UserData";
- return true;
-}
-
-unsigned int ON_ViewportUserData::SizeOf() const
-{
- unsigned int sz = ON_UserData::SizeOf();
- sz += sizeof(*this) - sizeof(ON_UserData);
- sz += m_description.SizeOf();
- return sz;
-}
-
-ON__UINT32 ON_ViewportUserData::DataCRC(ON__UINT32 current_remainder) const
-{
- current_remainder = m_description.DataCRC(current_remainder);
- return current_remainder;
-}
-
-void ON_ViewportUserData::Dump(ON_TextLog& text_log) const
-{
- const wchar_t* pszDescription = static_cast(m_description);
- if (nullptr == pszDescription)
- pszDescription = L"";
- text_log.Print("Description = \"%ls\"\n", pszDescription);
-}
-
-bool ON_ViewportUserData::WriteToArchive(const ON_BinaryArchive & archive, const ON_Object * parent_object) const
-{
- return (archive.Archive3dmVersion() >= 60);
-}
-
-bool ON_ViewportUserData::Write(ON_BinaryArchive& archive) const
-{
- bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0);
- if (!rc)
- return false;
-
- for (;;)
- {
- // 1.0 fields
- rc = archive.WriteString(m_description);
- if (!rc) break;
-
- break;
- }
-
- if (!archive.EndWrite3dmChunk())
- rc = false;
-
- return rc;
-}
-
-bool ON_ViewportUserData::Read(ON_BinaryArchive& archive)
-{
- int major_version = 0;
- int minor_version = 0;
- bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version);
- if (!rc)
- return false;
-
- for (;;)
- {
- rc = (1 == major_version);
- if (!rc) break;
-
- // 1.0 fields
- rc = archive.ReadString(m_description);
- if (!rc) break;
-
- break;
- }
-
- if (!archive.EndRead3dmChunk())
- rc = false;
-
- return rc;
-}
-
-ON_wString ON_Viewport::Description() const
-{
- ON_wString rc;
- ON_ViewportUserData* data = ON_ViewportUserData::FromObject(this);
- if (data)
- rc = data->m_description;
- return rc;
-}
-
-bool ON_Viewport::SetDescription(const wchar_t* description)
-{
- ON_ViewportUserData* data = ON_ViewportUserData::FromObject(this, true);
- if (nullptr == data)
- return false;
- data->m_description = description;
- return true;
-}
diff --git a/opennurbs_viewport.h b/opennurbs_viewport.h
index c278049b..60801584 100644
--- a/opennurbs_viewport.h
+++ b/opennurbs_viewport.h
@@ -1616,13 +1616,6 @@ public:
const ON_Xform* bbox_xform
) const;
- /*
- Description:
- Gets or sets the description, or contents, of the viewport.
- */
- ON_wString Description() const;
- bool SetDescription(const wchar_t* description);
-
protected:
// These boolean status flags are set to true when
diff --git a/opennurbs_wip.h b/opennurbs_wip.h
index b6f22fcf..980b12bd 100644
--- a/opennurbs_wip.h
+++ b/opennurbs_wip.h
@@ -17,13 +17,6 @@
#if !defined OPENNURBS_WIP_INC__
#define OPENNURBS_WIP_INC__
-// These SubD WIP defines can be deleted once we are 100% certain
-// they are not used in any code. They existed when SubD was a WIP
-// project that had to be conditionally added/removed from Rhino.
-#define OPENNURBS_SUBD_WIP
-#define RHINO_SUBD_WIP
-#define RHINO_SUBD_GRIPS_WIP
-
// Annotation table is being prototyped and on hold
// until V6 ships.
//#define OPENNURBS_ANNOTATION_TABLE_WIP
diff --git a/opennurbs_xform.cpp b/opennurbs_xform.cpp
index 4eef7c80..19c78c94 100644
--- a/opennurbs_xform.cpp
+++ b/opennurbs_xform.cpp
@@ -2087,6 +2087,38 @@ void ON_Xform::Mirror(
m_xform[3][3] = 1.0;
}
+const ON_Xform ON_Xform::MirrorTransformation(
+ ON_PlaneEquation mirror_plane
+)
+{
+ const ON_PlaneEquation e = mirror_plane.UnitizedPlaneEquation();
+ const ON_3dVector N(e.x, e.y, e.z);
+ ON_3dVector V = (-2.0*e.d)*N;
+ ON_Xform mirror;
+ mirror.m_xform[0][0] = 1 - 2.0*N.x*N.x;
+ mirror.m_xform[0][1] = -2.0*N.x*N.y;
+ mirror.m_xform[0][2] = -2.0*N.x*N.z;
+ mirror.m_xform[0][3] = V.x;
+
+ mirror.m_xform[1][0] = -2.0*N.y*N.x;
+ mirror.m_xform[1][1] = 1.0 - 2.0*N.y*N.y;
+ mirror.m_xform[1][2] = -2.0*N.y*N.z;
+ mirror.m_xform[1][3] = V.y;
+
+ mirror.m_xform[2][0] = -2.0*N.z*N.x;
+ mirror.m_xform[2][1] = -2.0*N.z*N.y;
+ mirror.m_xform[2][2] = 1.0 - 2.0*N.z*N.z;
+ mirror.m_xform[2][3] = V.z;
+
+ mirror.m_xform[3][0] = 0.0;
+ mirror.m_xform[3][1] = 0.0;
+ mirror.m_xform[3][2] = 0.0;
+ mirror.m_xform[3][3] = 1.0;
+
+ return mirror;
+}
+
+
bool ON_Xform::ChangeBasis(
diff --git a/opennurbs_xform.h b/opennurbs_xform.h
index 9a2b5254..eb20e748 100644
--- a/opennurbs_xform.h
+++ b/opennurbs_xform.h
@@ -838,6 +838,10 @@ Notes:
ON_3dVector normal_to_mirror_plane
);
+ static const ON_Xform MirrorTransformation(
+ ON_PlaneEquation mirror_plane
+ );
+
// Description: The ChangeBasis() function is overloaded
// and provides several
// ways to compute a change of basis transformation.
diff --git a/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj
index 66530269..d8fb7b38 100644
--- a/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj
+++ b/zlib/opennurbs_public_zlib.xcodeproj/project.pbxproj
@@ -181,8 +181,8 @@
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
- English,
en,
+ Base,
);
mainGroup = 1DB028141ED6430300FA9144;
productRefGroup = 1DB0281E1ED6430300FA9144 /* Products */;
diff --git a/zlib/zlib.vcxproj b/zlib/zlib.vcxproj
index 9298f70c..86ab9f51 100644
--- a/zlib/zlib.vcxproj
+++ b/zlib/zlib.vcxproj
@@ -47,30 +47,31 @@
{7B90C09F-DC78-42B2-AD34-380F6D466B29}
Win32Proj
zlib
+ 10.0
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
true
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
Unicode
StaticLibrary
false
- v141
+ v142
Unicode